linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 00/17] ARM: at91: move to common clk framework
@ 2013-11-12 20:51 Boris BREZILLON
  2013-11-12 20:55 ` [PATCH v5 01/17] ARM: at91: move at91_pmc.h to include/linux/clk/at91_pmc.h Boris BREZILLON
                   ` (17 more replies)
  0 siblings, 18 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 20:51 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

Hello,

This patch series is the 5th version of the at91 clk implementations
using the Common Clk Framework.

Most of the clock provided by the PMC (Power Management Controller) are
implemented :
- main clock (main oscillator)
- pll clocks
- master clock
- programmable clocks
- utmi clock
- peripheral clocks
- system clocks

Actually some clk drivers are missing (slow clk, main clk and processor
clk), and some drivers only implement a subset of the hardware capabilities
(master clk rate change is missing).
But this series implements all the already available clks, and I will hopefully
add missing features in a near future.

This implementation is only compatible with device tree definition.
The goal is to define the whole clock tree using the device tree.

BTW, could a dt maintainer take a look at these dt bindinds ?

Just a note to let you know these bindings are currently unstable and might
change in the future.


Mike, I know you already acked the previous version, but I slighty changed
the dt bindings, according to some feedbacks I got during ELCE.
Could you take a look at this new version (at least the new dt bindings),
and add your acked-by if you agree with these changes ?

Best Regards,
Boris

Changes since v4:
 - rework dt bindings:
   * replace "atmel,clk-id" property by the standard "reg" property
   * reference system, peripheral and programmable clks using the direct
     clk node instead of the parent node plus a clk id
 - provide a new helper function (of_at91_get_clk_range) to retrieve a clk
   range from the device tree

Changes since v3:
 - simplify master clk implementation (drop set_rate/parent support)
 - fix bug in set_rate function of pll driver
 - fix coding style issues
 - define macros and constants where needed
 - remove peripheral id macro references
 - remove sam9g35 specific handling (sam9g35 = sam9x5)
 - rework main clk prepare function to handle automatic rate calculation

Changes since v2:
 - fix several bugs in clk implementations
 - drop non-dt boards support
 - split the series to ease review and tests:
   * 1 patch series for new clk implementations (this series)
   * 1 patch series to move each at91 SoC to common clk framework (coming soon)
 - modify dt-bindings (add atmel,clk- prefix to atmel specific properties)
 - add clk macros for dt-bindings
 - add pmc framework (helper function to access pmc registers)
 - add interrupt support to enable passive wait in clk_prepare functions

Changes since v1:
 - fix bugs in pll, programmable and system clock implementations
   (wrong bit position).
 - add usb clock configuration support (ohci and udc drivers +
   clk_lookup for non dt boards)
 - rework of the system clock interfaces (no need to define a parent clock,
   system clock is a gate with no rate info)
 - change system, peripheral and programmable clk dt bindings (1 master node
   and multiple child nodes each defining a system/peripheral or prog clock)
 - fix bugs in sama5 dt definition

Boris BREZILLON (17):
  ARM: at91: move at91_pmc.h to include/linux/clk/at91_pmc.h
  ARM: at91: add Kconfig options for common clk support
  clk: at91: add PMC base support
  clk: at91: add PMC macro file for dt definitions
  clk: at91: add PMC main clock
  clk: at91: add PMC pll clocks
  clk: at91: add PMC master clock
  clk: at91: add PMC system clocks
  clk: at91: add PMC peripheral clocks
  clk: at91: add peripheral clk macros for peripheral clk dt bindings
  clk: at91: add PMC programmable clocks
  clk: at91: add PMC utmi clock
  clk: at91: add PMC usb clock
  clk: at91: add PMC smd clock
  clk: at91: add PMC clk device tree binding doc.
  ARM: at91: move pit timer to common clk framework
  ARM: at91: add new compatible strings for pmc driver

 .../devicetree/bindings/clock/at91-clock.txt       |  339 +++++++++++++
 arch/arm/mach-at91/Kconfig                         |   44 ++
 arch/arm/mach-at91/Kconfig.non_dt                  |    6 +
 arch/arm/mach-at91/Makefile                        |    2 +-
 arch/arm/mach-at91/at91rm9200.c                    |    2 +-
 arch/arm/mach-at91/at91sam9260.c                   |    2 +-
 arch/arm/mach-at91/at91sam9261.c                   |    2 +-
 arch/arm/mach-at91/at91sam9263.c                   |    2 +-
 arch/arm/mach-at91/at91sam926x_time.c              |   14 +-
 arch/arm/mach-at91/at91sam9g45.c                   |    2 +-
 arch/arm/mach-at91/at91sam9n12.c                   |    2 +-
 arch/arm/mach-at91/at91sam9rl.c                    |    2 +-
 arch/arm/mach-at91/at91sam9x5.c                    |    2 +-
 arch/arm/mach-at91/clock.c                         |    7 +-
 arch/arm/mach-at91/generic.h                       |    3 +-
 arch/arm/mach-at91/pm.c                            |    2 +-
 arch/arm/mach-at91/pm_slowclock.S                  |    2 +-
 arch/arm/mach-at91/sama5d3.c                       |    2 +-
 arch/arm/mach-at91/setup.c                         |    8 +-
 drivers/clk/Makefile                               |    1 +
 drivers/clk/at91/Makefile                          |   12 +
 drivers/clk/at91/clk-main.c                        |  189 +++++++
 drivers/clk/at91/clk-master.c                      |  272 ++++++++++
 drivers/clk/at91/clk-peripheral.c                  |  412 +++++++++++++++
 drivers/clk/at91/clk-pll.c                         |  534 ++++++++++++++++++++
 drivers/clk/at91/clk-plldiv.c                      |  137 +++++
 drivers/clk/at91/clk-programmable.c                |  368 ++++++++++++++
 drivers/clk/at91/clk-smd.c                         |  173 +++++++
 drivers/clk/at91/clk-system.c                      |  137 +++++
 drivers/clk/at91/clk-usb.c                         |  400 +++++++++++++++
 drivers/clk/at91/clk-utmi.c                        |  162 ++++++
 drivers/clk/at91/pmc.c                             |  399 +++++++++++++++
 drivers/clk/at91/pmc.h                             |  116 +++++
 drivers/usb/gadget/atmel_usba_udc.c                |    2 +-
 include/dt-bindings/clk/at91.h                     |   28 +
 .../include/mach => include/linux/clk}/at91_pmc.h  |    4 +-
 36 files changed, 3771 insertions(+), 20 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/at91-clock.txt
 create mode 100644 drivers/clk/at91/Makefile
 create mode 100644 drivers/clk/at91/clk-main.c
 create mode 100644 drivers/clk/at91/clk-master.c
 create mode 100644 drivers/clk/at91/clk-peripheral.c
 create mode 100644 drivers/clk/at91/clk-pll.c
 create mode 100644 drivers/clk/at91/clk-plldiv.c
 create mode 100644 drivers/clk/at91/clk-programmable.c
 create mode 100644 drivers/clk/at91/clk-smd.c
 create mode 100644 drivers/clk/at91/clk-system.c
 create mode 100644 drivers/clk/at91/clk-usb.c
 create mode 100644 drivers/clk/at91/clk-utmi.c
 create mode 100644 drivers/clk/at91/pmc.c
 create mode 100644 drivers/clk/at91/pmc.h
 create mode 100644 include/dt-bindings/clk/at91.h
 rename {arch/arm/mach-at91/include/mach => include/linux/clk}/at91_pmc.h (98%)

-- 
1.7.9.5


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

* [PATCH v5 01/17] ARM: at91: move at91_pmc.h to include/linux/clk/at91_pmc.h
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
@ 2013-11-12 20:55 ` Boris BREZILLON
  2013-11-12 20:57 ` [PATCH v5 02/17] ARM: at91: add Kconfig options for common clk support Boris BREZILLON
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 20:55 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch moves at91_pmc.h header from machine specific directory
(arch/arm/mach-at91/include/mach/at91_pmc.h) to clk include directory
(include/linux/clk/at91_pmc.h).
We need this to avoid reference to machine specific headers in clk
drivers.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Felipe Balbi <balbi@ti.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/at91rm9200.c                    |    2 +-
 arch/arm/mach-at91/at91sam9260.c                   |    2 +-
 arch/arm/mach-at91/at91sam9261.c                   |    2 +-
 arch/arm/mach-at91/at91sam9263.c                   |    2 +-
 arch/arm/mach-at91/at91sam9g45.c                   |    2 +-
 arch/arm/mach-at91/at91sam9n12.c                   |    2 +-
 arch/arm/mach-at91/at91sam9rl.c                    |    2 +-
 arch/arm/mach-at91/at91sam9x5.c                    |    2 +-
 arch/arm/mach-at91/clock.c                         |    2 +-
 arch/arm/mach-at91/pm.c                            |    2 +-
 arch/arm/mach-at91/pm_slowclock.S                  |    2 +-
 arch/arm/mach-at91/sama5d3.c                       |    2 +-
 arch/arm/mach-at91/setup.c                         |    2 +-
 drivers/usb/gadget/atmel_usba_udc.c                |    2 +-
 .../include/mach => include/linux/clk}/at91_pmc.h  |    2 +-
 15 files changed, 15 insertions(+), 15 deletions(-)
 rename {arch/arm/mach-at91/include/mach => include/linux/clk}/at91_pmc.h (99%)

diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c
index 4aad93d..102dac6 100644
--- a/arch/arm/mach-at91/at91rm9200.c
+++ b/arch/arm/mach-at91/at91rm9200.c
@@ -12,13 +12,13 @@
 
 #include <linux/module.h>
 #include <linux/reboot.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/irq.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <asm/system_misc.h>
 #include <mach/at91rm9200.h>
-#include <mach/at91_pmc.h>
 #include <mach/at91_st.h>
 #include <mach/cpu.h>
 
diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c
index 5de6074..16d7817 100644
--- a/arch/arm/mach-at91/at91sam9260.c
+++ b/arch/arm/mach-at91/at91sam9260.c
@@ -11,6 +11,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/proc-fns.h>
 #include <asm/irq.h>
@@ -20,7 +21,6 @@
 #include <mach/cpu.h>
 #include <mach/at91_dbgu.h>
 #include <mach/at91sam9260.h>
-#include <mach/at91_pmc.h>
 
 #include "at91_aic.h"
 #include "at91_rstc.h"
diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index 0e07932..c8f5958 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -11,6 +11,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/proc-fns.h>
 #include <asm/irq.h>
@@ -19,7 +20,6 @@
 #include <asm/system_misc.h>
 #include <mach/cpu.h>
 #include <mach/at91sam9261.h>
-#include <mach/at91_pmc.h>
 
 #include "at91_aic.h"
 #include "at91_rstc.h"
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index 6ce7d18..cd8ad95 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -11,6 +11,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/proc-fns.h>
 #include <asm/irq.h>
@@ -18,7 +19,6 @@
 #include <asm/mach/map.h>
 #include <asm/system_misc.h>
 #include <mach/at91sam9263.h>
-#include <mach/at91_pmc.h>
 
 #include "at91_aic.h"
 #include "at91_rstc.h"
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index 474ee04..9794b20 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -12,13 +12,13 @@
 
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/irq.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <asm/system_misc.h>
 #include <mach/at91sam9g45.h>
-#include <mach/at91_pmc.h>
 #include <mach/cpu.h>
 
 #include "at91_aic.h"
diff --git a/arch/arm/mach-at91/at91sam9n12.c b/arch/arm/mach-at91/at91sam9n12.c
index 2d895a2..deafab2 100644
--- a/arch/arm/mach-at91/at91sam9n12.c
+++ b/arch/arm/mach-at91/at91sam9n12.c
@@ -8,12 +8,12 @@
 
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/irq.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <mach/at91sam9n12.h>
-#include <mach/at91_pmc.h>
 #include <mach/cpu.h>
 
 #include "board.h"
diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
index d4ec0d9..947da95 100644
--- a/arch/arm/mach-at91/at91sam9rl.c
+++ b/arch/arm/mach-at91/at91sam9rl.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/proc-fns.h>
 #include <asm/irq.h>
@@ -19,7 +20,6 @@
 #include <mach/cpu.h>
 #include <mach/at91_dbgu.h>
 #include <mach/at91sam9rl.h>
-#include <mach/at91_pmc.h>
 
 #include "at91_aic.h"
 #include "at91_rstc.h"
diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c
index 916e5a1..a930d73 100644
--- a/arch/arm/mach-at91/at91sam9x5.c
+++ b/arch/arm/mach-at91/at91sam9x5.c
@@ -8,12 +8,12 @@
 
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/irq.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <mach/at91sam9x5.h>
-#include <mach/at91_pmc.h>
 #include <mach/cpu.h>
 
 #include "board.h"
diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c
index 6b2630a..5f02aea 100644
--- a/arch/arm/mach-at91/clock.c
+++ b/arch/arm/mach-at91/clock.c
@@ -24,9 +24,9 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/of_address.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <mach/hardware.h>
-#include <mach/at91_pmc.h>
 #include <mach/cpu.h>
 
 #include <asm/proc-fns.h>
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 15afb5d..692cacd 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -19,13 +19,13 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/irq.h>
 #include <linux/atomic.h>
 #include <asm/mach/time.h>
 #include <asm/mach/irq.h>
 
-#include <mach/at91_pmc.h>
 #include <mach/cpu.h>
 
 #include "at91_aic.h"
diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S
index 098c28d..2001877 100644
--- a/arch/arm/mach-at91/pm_slowclock.S
+++ b/arch/arm/mach-at91/pm_slowclock.S
@@ -13,8 +13,8 @@
  */
 
 #include <linux/linkage.h>
+#include <linux/clk/at91_pmc.h>
 #include <mach/hardware.h>
-#include <mach/at91_pmc.h>
 #include <mach/at91_ramc.h>
 
 
diff --git a/arch/arm/mach-at91/sama5d3.c b/arch/arm/mach-at91/sama5d3.c
index 4012797..0003949 100644
--- a/arch/arm/mach-at91/sama5d3.c
+++ b/arch/arm/mach-at91/sama5d3.c
@@ -9,12 +9,12 @@
 
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/irq.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <mach/sama5d3.h>
-#include <mach/at91_pmc.h>
 #include <mach/cpu.h>
 
 #include "soc.h"
diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
index b17fbcf..41aa68b 100644
--- a/arch/arm/mach-at91/setup.c
+++ b/arch/arm/mach-at91/setup.c
@@ -11,6 +11,7 @@
 #include <linux/pm.h>
 #include <linux/of_address.h>
 #include <linux/pinctrl/machine.h>
+#include <linux/clk/at91_pmc.h>
 
 #include <asm/system_misc.h>
 #include <asm/mach/map.h>
@@ -18,7 +19,6 @@
 #include <mach/hardware.h>
 #include <mach/cpu.h>
 #include <mach/at91_dbgu.h>
-#include <mach/at91_pmc.h>
 
 #include "at91_shdwc.h"
 #include "soc.h"
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index 2cb52e0..9f71d9f 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -326,7 +326,7 @@ static int vbus_is_present(struct usba_udc *udc)
 
 #if defined(CONFIG_ARCH_AT91SAM9RL)
 
-#include <mach/at91_pmc.h>
+#include <linux/clk/at91_pmc.h>
 
 static void toggle_bias(int is_on)
 {
diff --git a/arch/arm/mach-at91/include/mach/at91_pmc.h b/include/linux/clk/at91_pmc.h
similarity index 99%
rename from arch/arm/mach-at91/include/mach/at91_pmc.h
rename to include/linux/clk/at91_pmc.h
index c604cc6..00c45b3 100644
--- a/arch/arm/mach-at91/include/mach/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -1,5 +1,5 @@
 /*
- * arch/arm/mach-at91/include/mach/at91_pmc.h
+ * include/linux/clk/at91_pmc.h
  *
  * Copyright (C) 2005 Ivan Kokshaysky
  * Copyright (C) SAN People
-- 
1.7.9.5


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

* [PATCH v5 02/17] ARM: at91: add Kconfig options for common clk support
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
  2013-11-12 20:55 ` [PATCH v5 01/17] ARM: at91: move at91_pmc.h to include/linux/clk/at91_pmc.h Boris BREZILLON
@ 2013-11-12 20:57 ` Boris BREZILLON
  2013-11-12 21:02 ` [PATCH v5 03/17] clk: at91: add PMC base support Boris BREZILLON
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 20:57 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds the following Kconfig options to prepare the transition to
common clk framework:

- AT91_USE_OLD_CLK: this option is selected by every SoC which does not
  support new at91 clks based on common clk framework (SoC which does not
  define the clock tree in its device tree).
  This options is also selected when the user choose non dt boards support
  (new at91 clks can only be registered from a device tree definition).

- COMMON_CLK_AT91: this option cannot be selected directly. Instead it is
  enabled if these 3 conditions are met:
   * at least one of the selected SoCs have a PMC (Power Management
     Controller) Unit
   * device tree support is enabled
   * the old at91 clk implementation is disabled (every selected SoC define
     its clks in its device tree and non dt boards support is disabled)

- OLD_CLK_AT91: this option cannot be selected directly. Instead it is
  enabled if these 2 conditions are met:
   * at least one of the selected SoCs have a PMC (Power Management
     Controller) Unit
   * at least one of the selected SoCs does not define its clks in its
     device tree or non dt-boards support is enabled

This patch selects AT91_USE_OLD_CLK in all currently supported SoCs. These
selects will be removed after clk definitions are properly added in each
soc's device tree.
It also selects AT91_USE_OLD_CLK in all non-dt boards support.

AT91_PMC_UNIT references are replaced by OLD_CLK_AT91, because PMC Unit is
enabled for both old and common clk implementations, and old clk
implementation should not be compiled if COMMON_CLK is enabled.

To avoid future link errors, a new stub is created for at91_dt_clock_init
function if OLD_CLK_AT91 is disabled.

A new check is added in dt init functions (setup.c) to prepare for SoCs
supporting new clk implementation. These SoCs won't setup the
register_clocks callback (clk registration is done using of_clk_init).

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/Kconfig        |   21 +++++++++++++++++++++
 arch/arm/mach-at91/Kconfig.non_dt |    6 ++++++
 arch/arm/mach-at91/Makefile       |    2 +-
 arch/arm/mach-at91/generic.h      |    3 ++-
 arch/arm/mach-at91/setup.c        |    6 ++++--
 5 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 699b71e..85b53a4 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -6,10 +6,22 @@ config HAVE_AT91_DBGU0
 config HAVE_AT91_DBGU1
 	bool
 
+config AT91_USE_OLD_CLK
+	bool
+
 config AT91_PMC_UNIT
 	bool
 	default !ARCH_AT91X40
 
+config COMMON_CLK_AT91
+	bool
+	default AT91_PMC_UNIT && USE_OF && !AT91_USE_OLD_CLK
+	select COMMON_CLK
+
+config OLD_CLK_AT91
+	bool
+	default AT91_PMC_UNIT && AT91_USE_OLD_CLK
+
 config AT91_SAM9_ALT_RESET
 	bool
 	default !ARCH_AT91X40
@@ -65,6 +77,7 @@ config SOC_SAMA5D3
 	select SOC_SAMA5
 	select HAVE_FB_ATMEL
 	select HAVE_AT91_DBGU1
+	select AT91_USE_OLD_CLK
 	help
 	  Select this if you are using one of Atmel's SAMA5D3 family SoC.
 	  This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35.
@@ -78,11 +91,13 @@ config SOC_AT91RM9200
 	select HAVE_AT91_DBGU0
 	select MULTI_IRQ_HANDLER
 	select SPARSE_IRQ
+	select AT91_USE_OLD_CLK
 
 config SOC_AT91SAM9260
 	bool "AT91SAM9260, AT91SAM9XE or AT91SAM9G20"
 	select HAVE_AT91_DBGU0
 	select SOC_AT91SAM9
+	select AT91_USE_OLD_CLK
 	help
 	  Select this if you are using one of Atmel's AT91SAM9260, AT91SAM9XE
 	  or AT91SAM9G20 SoC.
@@ -92,6 +107,7 @@ config SOC_AT91SAM9261
 	select HAVE_AT91_DBGU0
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
+	select AT91_USE_OLD_CLK
 	help
 	  Select this if you are using one of Atmel's AT91SAM9261 or AT91SAM9G10 SoC.
 
@@ -100,18 +116,21 @@ config SOC_AT91SAM9263
 	select HAVE_AT91_DBGU1
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
+	select AT91_USE_OLD_CLK
 
 config SOC_AT91SAM9RL
 	bool "AT91SAM9RL"
 	select HAVE_AT91_DBGU0
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
+	select AT91_USE_OLD_CLK
 
 config SOC_AT91SAM9G45
 	bool "AT91SAM9G45 or AT91SAM9M10 families"
 	select HAVE_AT91_DBGU1
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
+	select AT91_USE_OLD_CLK
 	help
 	  Select this if you are using one of Atmel's AT91SAM9G45 family SoC.
 	  This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11.
@@ -121,6 +140,7 @@ config SOC_AT91SAM9X5
 	select HAVE_AT91_DBGU0
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
+	select AT91_USE_OLD_CLK
 	help
 	  Select this if you are using one of Atmel's AT91SAM9x5 family SoC.
 	  This means that your SAM9 name finishes with a '5' (except if it is
@@ -133,6 +153,7 @@ config SOC_AT91SAM9N12
 	select HAVE_AT91_DBGU0
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
+	select AT91_USE_OLD_CLK
 	help
 	  Select this if you are using Atmel's AT91SAM9N12 SoC.
 
diff --git a/arch/arm/mach-at91/Kconfig.non_dt b/arch/arm/mach-at91/Kconfig.non_dt
index ca900be..b736b57 100644
--- a/arch/arm/mach-at91/Kconfig.non_dt
+++ b/arch/arm/mach-at91/Kconfig.non_dt
@@ -12,26 +12,32 @@ config ARCH_AT91_NONE
 config ARCH_AT91RM9200
 	bool "AT91RM9200"
 	select SOC_AT91RM9200
+	select AT91_USE_OLD_CLK
 
 config ARCH_AT91SAM9260
 	bool "AT91SAM9260 or AT91SAM9XE or AT91SAM9G20"
 	select SOC_AT91SAM9260
+	select AT91_USE_OLD_CLK
 
 config ARCH_AT91SAM9261
 	bool "AT91SAM9261 or AT91SAM9G10"
 	select SOC_AT91SAM9261
+	select AT91_USE_OLD_CLK
 
 config ARCH_AT91SAM9263
 	bool "AT91SAM9263"
 	select SOC_AT91SAM9263
+	select AT91_USE_OLD_CLK
 
 config ARCH_AT91SAM9RL
 	bool "AT91SAM9RL"
 	select SOC_AT91SAM9RL
+	select AT91_USE_OLD_CLK
 
 config ARCH_AT91SAM9G45
 	bool "AT91SAM9G45"
 	select SOC_AT91SAM9G45
+	select AT91_USE_OLD_CLK
 
 config ARCH_AT91X40
 	bool "AT91x40"
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile
index 3b0a953..8539411 100644
--- a/arch/arm/mach-at91/Makefile
+++ b/arch/arm/mach-at91/Makefile
@@ -7,7 +7,7 @@ obj-m		:=
 obj-n		:=
 obj-		:=
 
-obj-$(CONFIG_AT91_PMC_UNIT)	+= clock.o
+obj-$(CONFIG_OLD_CLK_AT91)	+= clock.o
 obj-$(CONFIG_AT91_SAM9_ALT_RESET) += at91sam9_alt_reset.o
 obj-$(CONFIG_AT91_SAM9G45_RESET) += at91sam9g45_reset.o
 obj-$(CONFIG_AT91_SAM9_TIME)	+= at91sam926x_time.o
diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h
index dc6e2f5..375c42e 100644
--- a/arch/arm/mach-at91/generic.h
+++ b/arch/arm/mach-at91/generic.h
@@ -44,11 +44,12 @@ extern void at91sam926x_pit_init(void);
 extern void at91x40_timer_init(void);
 
  /* Clocks */
-#ifdef CONFIG_AT91_PMC_UNIT
+#ifdef CONFIG_OLD_CLK_AT91
 extern int __init at91_clock_init(unsigned long main_clock);
 extern int __init at91_dt_clock_init(void);
 #else
 static int inline at91_clock_init(unsigned long main_clock) { return 0; }
+static int inline at91_dt_clock_init(void) { return 0; }
 #endif
 struct device;
 
diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
index 41aa68b..06052fc 100644
--- a/arch/arm/mach-at91/setup.c
+++ b/arch/arm/mach-at91/setup.c
@@ -483,7 +483,8 @@ void __init at91rm9200_dt_initialize(void)
 	at91_dt_clock_init();
 
 	/* Register the processor-specific clocks */
-	at91_boot_soc.register_clocks();
+	if (at91_boot_soc.register_clocks)
+		at91_boot_soc.register_clocks();
 
 	at91_boot_soc.init();
 }
@@ -498,7 +499,8 @@ void __init at91_dt_initialize(void)
 	at91_dt_clock_init();
 
 	/* Register the processor-specific clocks */
-	at91_boot_soc.register_clocks();
+	if (at91_boot_soc.register_clocks)
+		at91_boot_soc.register_clocks();
 
 	if (at91_boot_soc.init)
 		at91_boot_soc.init();
-- 
1.7.9.5


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

* [PATCH v5 03/17] clk: at91: add PMC base support
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
  2013-11-12 20:55 ` [PATCH v5 01/17] ARM: at91: move at91_pmc.h to include/linux/clk/at91_pmc.h Boris BREZILLON
  2013-11-12 20:57 ` [PATCH v5 02/17] ARM: at91: add Kconfig options for common clk support Boris BREZILLON
@ 2013-11-12 21:02 ` Boris BREZILLON
  2013-11-12 21:04 ` [PATCH v5 04/17] clk: at91: add PMC macro file for dt definitions Boris BREZILLON
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 21:02 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds at91 PMC (Power Management Controller) base support.

All at91 clocks managed by the PMC unit will use this framework.

This framework provides the following fonctionalities:
- define a new struct at91_pmc to hide PMC internals (lock, PMC memory
  mapping, irq domain, ...)
- read/write helper functions (pmc_read/write) to access PMC registers
- lock/unlock helper functions (pmc_lock/unlock) to lock/unlock access to
  pmc registers
- a new irq domain and its associated irq chip to request PMC specific
  interrupts (useful for clk prepare callbacks)

The PMC unit is declared as a dt clk provider (CLK_OF_DECLARE), and every
clk using this framework will declare a table of of_at91_clk_init_cb_t
and add it to the pmc_clk_ids table.

When the pmc dt clock setup function is called (by of_clk_init function),
it triggers the registration of every supported child clk (those matching
the definitions in pmc_clk_ids).

This patch copies at91_pmc_base (memory mapping) and at91sam9_idle
(function) from arch/arm/mach-at91/clock.c (which is not compiled if
COMMON_CLK_AT91 is enabled).

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 drivers/clk/Makefile      |    1 +
 drivers/clk/at91/Makefile |    5 +
 drivers/clk/at91/pmc.c    |  306 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h    |   61 +++++++++
 4 files changed, 373 insertions(+)
 create mode 100644 drivers/clk/at91/Makefile
 create mode 100644 drivers/clk/at91/pmc.c
 create mode 100644 drivers/clk/at91/pmc.h

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 7b11106..28c2678 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o
 obj-$(CONFIG_ARCH_ZYNQ)		+= zynq/
 obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
 obj-$(CONFIG_PLAT_SAMSUNG)	+= samsung/
+obj-$(CONFIG_COMMON_CLK_AT91)	+= at91/
 
 obj-$(CONFIG_X86)		+= x86/
 
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
new file mode 100644
index 0000000..1d4fb21
--- /dev/null
+++ b/drivers/clk/at91/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for at91 specific clk
+#
+
+obj-y += pmc.o
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
new file mode 100644
index 0000000..c8a9a63
--- /dev/null
+++ b/drivers/clk/at91/pmc.c
@@ -0,0 +1,306 @@
+/*
+ * drivers/clk/at91/pmc.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_irq.h>
+
+#include <asm/proc-fns.h>
+
+#include "pmc.h"
+
+void __iomem *at91_pmc_base;
+EXPORT_SYMBOL_GPL(at91_pmc_base);
+
+void at91sam9_idle(void)
+{
+	at91_pmc_write(AT91_PMC_SCDR, AT91_PMC_PCK);
+	cpu_do_idle();
+}
+
+int of_at91_get_clk_range(struct device_node *np, const char *propname,
+			  struct clk_range *range)
+{
+	u32 min, max;
+	int ret;
+
+	ret = of_property_read_u32_index(np, propname, 0, &min);
+	if (ret)
+		return ret;
+
+	ret = of_property_read_u32_index(np, propname, 1, &max);
+	if (ret)
+		return ret;
+
+	if (range) {
+		range->min = min;
+		range->max = max;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
+
+static void pmc_irq_mask(struct irq_data *d)
+{
+	struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
+
+	pmc_write(pmc, AT91_PMC_IDR, 1 << d->hwirq);
+}
+
+static void pmc_irq_unmask(struct irq_data *d)
+{
+	struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
+
+	pmc_write(pmc, AT91_PMC_IER, 1 << d->hwirq);
+}
+
+static int pmc_irq_set_type(struct irq_data *d, unsigned type)
+{
+	if (type != IRQ_TYPE_LEVEL_HIGH) {
+		pr_warn("PMC: type not supported (support only IRQ_TYPE_LEVEL_HIGH type)\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct irq_chip pmc_irq = {
+	.name = "PMC",
+	.irq_disable = pmc_irq_mask,
+	.irq_mask = pmc_irq_mask,
+	.irq_unmask = pmc_irq_unmask,
+	.irq_set_type = pmc_irq_set_type,
+};
+
+static struct lock_class_key pmc_lock_class;
+
+static int pmc_irq_map(struct irq_domain *h, unsigned int virq,
+		       irq_hw_number_t hw)
+{
+	struct at91_pmc	*pmc = h->host_data;
+
+	irq_set_lockdep_class(virq, &pmc_lock_class);
+
+	irq_set_chip_and_handler(virq, &pmc_irq,
+				 handle_level_irq);
+	set_irq_flags(virq, IRQF_VALID);
+	irq_set_chip_data(virq, pmc);
+
+	return 0;
+}
+
+static int pmc_irq_domain_xlate(struct irq_domain *d,
+				struct device_node *ctrlr,
+				const u32 *intspec, unsigned int intsize,
+				irq_hw_number_t *out_hwirq,
+				unsigned int *out_type)
+{
+	struct at91_pmc *pmc = d->host_data;
+	const struct at91_pmc_caps *caps = pmc->caps;
+
+	if (WARN_ON(intsize < 1))
+		return -EINVAL;
+
+	*out_hwirq = intspec[0];
+
+	if (!(caps->available_irqs & (1 << *out_hwirq)))
+		return -EINVAL;
+
+	*out_type = IRQ_TYPE_LEVEL_HIGH;
+
+	return 0;
+}
+
+static struct irq_domain_ops pmc_irq_ops = {
+	.map	= pmc_irq_map,
+	.xlate	= pmc_irq_domain_xlate,
+};
+
+static irqreturn_t pmc_irq_handler(int irq, void *data)
+{
+	struct at91_pmc *pmc = (struct at91_pmc *)data;
+	unsigned long sr;
+	int n;
+
+	sr = pmc_read(pmc, AT91_PMC_SR) & pmc_read(pmc, AT91_PMC_IMR);
+	if (!sr)
+		return IRQ_NONE;
+
+	for_each_set_bit(n, &sr, BITS_PER_LONG)
+		generic_handle_irq(irq_find_mapping(pmc->irqdomain, n));
+
+	return IRQ_HANDLED;
+}
+
+static const struct at91_pmc_caps at91rm9200_caps = {
+	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
+			  AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
+			  AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY |
+			  AT91_PMC_PCK3RDY,
+};
+
+static const struct at91_pmc_caps at91sam9260_caps = {
+	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
+			  AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
+			  AT91_PMC_PCK1RDY,
+};
+
+static const struct at91_pmc_caps at91sam9g45_caps = {
+	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
+			  AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
+			  AT91_PMC_PCK1RDY,
+};
+
+static const struct at91_pmc_caps at91sam9n12_caps = {
+	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
+			  AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
+			  AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS |
+			  AT91_PMC_MOSCRCS | AT91_PMC_CFDEV,
+};
+
+static const struct at91_pmc_caps at91sam9x5_caps = {
+	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
+			  AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
+			  AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS |
+			  AT91_PMC_MOSCRCS | AT91_PMC_CFDEV,
+};
+
+static const struct at91_pmc_caps sama5d3_caps = {
+	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
+			  AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
+			  AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY |
+			  AT91_PMC_MOSCSELS | AT91_PMC_MOSCRCS |
+			  AT91_PMC_CFDEV,
+};
+
+static struct at91_pmc *__init at91_pmc_init(struct device_node *np,
+					     void __iomem *regbase, int virq,
+					     const struct at91_pmc_caps *caps)
+{
+	struct at91_pmc *pmc;
+
+	if (!regbase || !virq ||  !caps)
+		return NULL;
+
+	at91_pmc_base = regbase;
+
+	pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
+	if (!pmc)
+		return NULL;
+
+	spin_lock_init(&pmc->lock);
+	pmc->regbase = regbase;
+	pmc->virq = virq;
+	pmc->caps = caps;
+
+	pmc->irqdomain = irq_domain_add_linear(np, 32, &pmc_irq_ops, pmc);
+
+	if (!pmc->irqdomain)
+		goto out_free_pmc;
+
+	pmc_write(pmc, AT91_PMC_IDR, 0xffffffff);
+	if (request_irq(pmc->virq, pmc_irq_handler, IRQF_SHARED, "pmc", pmc))
+		goto out_remove_irqdomain;
+
+	return pmc;
+
+out_remove_irqdomain:
+	irq_domain_remove(pmc->irqdomain);
+out_free_pmc:
+	kfree(pmc);
+
+	return NULL;
+}
+
+static const struct of_device_id pmc_clk_ids[] __initdata = {
+	{ /*sentinel*/ }
+};
+
+static void __init of_at91_pmc_setup(struct device_node *np,
+				     const struct at91_pmc_caps *caps)
+{
+	struct at91_pmc *pmc;
+	struct device_node *childnp;
+	void (*clk_setup)(struct device_node *, struct at91_pmc *);
+	const struct of_device_id *clk_id;
+	void __iomem *regbase = of_iomap(np, 0);
+	int virq;
+
+	if (!regbase)
+		return;
+
+	virq = irq_of_parse_and_map(np, 0);
+	if (!virq)
+		return;
+
+	pmc = at91_pmc_init(np, regbase, virq, caps);
+	if (!pmc)
+		return;
+	for_each_child_of_node(np, childnp) {
+		clk_id = of_match_node(pmc_clk_ids, childnp);
+		if (!clk_id)
+			continue;
+		clk_setup = clk_id->data;
+		clk_setup(childnp, pmc);
+	}
+}
+
+static void __init of_at91rm9200_pmc_setup(struct device_node *np)
+{
+	of_at91_pmc_setup(np, &at91rm9200_caps);
+}
+CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-pmc",
+	       of_at91rm9200_pmc_setup);
+
+static void __init of_at91sam9260_pmc_setup(struct device_node *np)
+{
+	of_at91_pmc_setup(np, &at91sam9260_caps);
+}
+CLK_OF_DECLARE(at91sam9260_clk_main, "atmel,at91sam9260-pmc",
+	       of_at91sam9260_pmc_setup);
+
+static void __init of_at91sam9g45_pmc_setup(struct device_node *np)
+{
+	of_at91_pmc_setup(np, &at91sam9g45_caps);
+}
+CLK_OF_DECLARE(at91sam9g45_clk_main, "atmel,at91sam9g45-pmc",
+	       of_at91sam9g45_pmc_setup);
+
+static void __init of_at91sam9n12_pmc_setup(struct device_node *np)
+{
+	of_at91_pmc_setup(np, &at91sam9n12_caps);
+}
+CLK_OF_DECLARE(at91sam9n12_clk_main, "atmel,at91sam9n12-pmc",
+	       of_at91sam9n12_pmc_setup);
+
+static void __init of_at91sam9x5_pmc_setup(struct device_node *np)
+{
+	of_at91_pmc_setup(np, &at91sam9x5_caps);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-pmc",
+	       of_at91sam9x5_pmc_setup);
+
+static void __init of_sama5d3_pmc_setup(struct device_node *np)
+{
+	of_at91_pmc_setup(np, &sama5d3_caps);
+}
+CLK_OF_DECLARE(sama5d3_clk_main, "atmel,sama5d3-pmc",
+	       of_sama5d3_pmc_setup);
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
new file mode 100644
index 0000000..d92b946
--- /dev/null
+++ b/drivers/clk/at91/pmc.h
@@ -0,0 +1,61 @@
+/*
+ * drivers/clk/at91/pmc.h
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PMC_H_
+#define __PMC_H_
+
+#include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/spinlock.h>
+
+struct clk_range {
+	unsigned long min;
+	unsigned long max;
+};
+
+#define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,}
+
+struct at91_pmc_caps {
+	u32 available_irqs;
+};
+
+struct at91_pmc {
+	void __iomem *regbase;
+	int virq;
+	spinlock_t lock;
+	const struct at91_pmc_caps *caps;
+	struct irq_domain *irqdomain;
+};
+
+static inline void pmc_lock(struct at91_pmc *pmc)
+{
+	spin_lock(&pmc->lock);
+}
+
+static inline void pmc_unlock(struct at91_pmc *pmc)
+{
+	spin_unlock(&pmc->lock);
+}
+
+static inline u32 pmc_read(struct at91_pmc *pmc, int offset)
+{
+	return readl(pmc->regbase + offset);
+}
+
+static inline void pmc_write(struct at91_pmc *pmc, int offset, u32 value)
+{
+	writel(value, pmc->regbase + offset);
+}
+
+int of_at91_get_clk_range(struct device_node *np, const char *propname,
+			  struct clk_range *range);
+
+#endif /* __PMC_H_ */
-- 
1.7.9.5


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

* [PATCH v5 04/17] clk: at91: add PMC macro file for dt definitions
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (2 preceding siblings ...)
  2013-11-12 21:02 ` [PATCH v5 03/17] clk: at91: add PMC base support Boris BREZILLON
@ 2013-11-12 21:04 ` Boris BREZILLON
  2013-11-12 21:54 ` [PATCH v5 05/17] clk: at91: add PMC main clock Boris BREZILLON
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 21:04 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds a new macro file for PMC macros.

This macro file includes the definitions of SR (status register) bit
offsets and will be use to reference PMC irqs.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 include/dt-bindings/clk/at91.h |   22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 include/dt-bindings/clk/at91.h

diff --git a/include/dt-bindings/clk/at91.h b/include/dt-bindings/clk/at91.h
new file mode 100644
index 0000000..0b4cb99
--- /dev/null
+++ b/include/dt-bindings/clk/at91.h
@@ -0,0 +1,22 @@
+/*
+ * This header provides constants for AT91 pmc status.
+ *
+ * The constants defined in this header are being used in dts.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef _DT_BINDINGS_CLK_AT91_H
+#define _DT_BINDINGS_CLK_AT91_H
+
+#define AT91_PMC_MOSCS		0		/* MOSCS Flag */
+#define AT91_PMC_LOCKA		1		/* PLLA Lock */
+#define AT91_PMC_LOCKB		2		/* PLLB Lock */
+#define AT91_PMC_MCKRDY		3		/* Master Clock */
+#define AT91_PMC_LOCKU		6		/* UPLL Lock */
+#define AT91_PMC_PCKRDY(id)	(8 + (id))	/* Programmable Clock */
+#define AT91_PMC_MOSCSELS	16		/* Main Oscillator Selection */
+#define AT91_PMC_MOSCRCS	17		/* Main On-Chip RC */
+#define AT91_PMC_CFDEV		18		/* Clock Failure Detector Event */
+
+#endif
-- 
1.7.9.5


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

* [PATCH v5 05/17] clk: at91: add PMC main clock
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (3 preceding siblings ...)
  2013-11-12 21:04 ` [PATCH v5 04/17] clk: at91: add PMC macro file for dt definitions Boris BREZILLON
@ 2013-11-12 21:54 ` Boris BREZILLON
  2013-11-12 21:57 ` [PATCH v5 06/17] clk: at91: add PMC pll clocks Boris BREZILLON
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 21:54 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds new at91 main oscillator clock implementation using common
clk framework.

If rate is not provided during clock registration it is calculated using
the slow clock (main clk parent in this case) rate and MCFR register.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 drivers/clk/at91/Makefile   |    1 +
 drivers/clk/at91/clk-main.c |  189 +++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.c      |    5 ++
 drivers/clk/at91/pmc.h      |    3 +
 4 files changed, 198 insertions(+)
 create mode 100644 drivers/clk/at91/clk-main.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 1d4fb21..44105bd 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-y += pmc.o
+obj-y += clk-main.o
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
new file mode 100644
index 0000000..905ceff
--- /dev/null
+++ b/drivers/clk/at91/clk-main.c
@@ -0,0 +1,189 @@
+/*
+ * drivers/clk/at91/clk-main.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "pmc.h"
+
+#define SLOW_CLOCK_FREQ		32768
+#define MAINF_DIV		16
+#define MAINFRDY_TIMEOUT	(((MAINF_DIV + 1) * USEC_PER_SEC) / \
+				 SLOW_CLOCK_FREQ)
+#define MAINF_LOOP_MIN_WAIT	(USEC_PER_SEC / SLOW_CLOCK_FREQ)
+#define MAINF_LOOP_MAX_WAIT	MAINFRDY_TIMEOUT
+
+struct clk_main {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+	unsigned long rate;
+	unsigned int irq;
+	wait_queue_head_t wait;
+};
+
+#define to_clk_main(hw) container_of(hw, struct clk_main, hw)
+
+static irqreturn_t clk_main_irq_handler(int irq, void *dev_id)
+{
+	struct clk_main *clkmain = (struct clk_main *)dev_id;
+
+	wake_up(&clkmain->wait);
+	disable_irq_nosync(clkmain->irq);
+
+	return IRQ_HANDLED;
+}
+
+static int clk_main_prepare(struct clk_hw *hw)
+{
+	struct clk_main *clkmain = to_clk_main(hw);
+	struct at91_pmc *pmc = clkmain->pmc;
+	unsigned long halt_time, timeout;
+	u32 tmp;
+
+	while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCS)) {
+		enable_irq(clkmain->irq);
+		wait_event(clkmain->wait,
+			   pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCS);
+	}
+
+	if (clkmain->rate)
+		return 0;
+
+	timeout = jiffies + usecs_to_jiffies(MAINFRDY_TIMEOUT);
+	do {
+		halt_time = jiffies;
+		tmp = pmc_read(pmc, AT91_CKGR_MCFR);
+		if (tmp & AT91_PMC_MAINRDY)
+			return 0;
+		usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
+	} while (time_before(halt_time, timeout));
+
+	return 0;
+}
+
+static int clk_main_is_prepared(struct clk_hw *hw)
+{
+	struct clk_main *clkmain = to_clk_main(hw);
+
+	return !!(pmc_read(clkmain->pmc, AT91_PMC_SR) & AT91_PMC_MOSCS);
+}
+
+static unsigned long clk_main_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	u32 tmp;
+	struct clk_main *clkmain = to_clk_main(hw);
+	struct at91_pmc *pmc = clkmain->pmc;
+
+	if (clkmain->rate)
+		return clkmain->rate;
+
+	tmp = pmc_read(pmc, AT91_CKGR_MCFR) & AT91_PMC_MAINF;
+	clkmain->rate = (tmp * parent_rate) / MAINF_DIV;
+
+	return clkmain->rate;
+}
+
+static const struct clk_ops main_ops = {
+	.prepare = clk_main_prepare,
+	.is_prepared = clk_main_is_prepared,
+	.recalc_rate = clk_main_recalc_rate,
+};
+
+static struct clk * __init
+at91_clk_register_main(struct at91_pmc *pmc,
+		       unsigned int irq,
+		       const char *name,
+		       const char *parent_name,
+		       unsigned long rate)
+{
+	int ret;
+	struct clk_main *clkmain;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	if (!pmc || !irq || !name)
+		return ERR_PTR(-EINVAL);
+
+	if (!rate && !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
+	if (!clkmain)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &main_ops;
+	init.parent_names = parent_name ? &parent_name : NULL;
+	init.num_parents = parent_name ? 1 : 0;
+	init.flags = parent_name ? 0 : CLK_IS_ROOT;
+
+	clkmain->hw.init = &init;
+	clkmain->rate = rate;
+	clkmain->pmc = pmc;
+	clkmain->irq = irq;
+	init_waitqueue_head(&clkmain->wait);
+	irq_set_status_flags(clkmain->irq, IRQ_NOAUTOEN);
+	ret = request_irq(clkmain->irq, clk_main_irq_handler,
+			  IRQF_TRIGGER_HIGH, "clk-main", clkmain);
+	if (ret)
+		return ERR_PTR(ret);
+
+	clk = clk_register(NULL, &clkmain->hw);
+	if (IS_ERR(clk)) {
+		free_irq(clkmain->irq, clkmain);
+		kfree(clkmain);
+	}
+
+	return clk;
+}
+
+
+
+static void __init
+of_at91_clk_main_setup(struct device_node *np, struct at91_pmc *pmc)
+{
+	struct clk *clk;
+	unsigned int irq;
+	const char *parent_name;
+	const char *name = np->name;
+	u32 rate = 0;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	of_property_read_string(np, "clock-output-names", &name);
+	of_property_read_u32(np, "clock-frequency", &rate);
+	irq = irq_of_parse_and_map(np, 0);
+	if (!irq)
+		return;
+
+	clk = at91_clk_register_main(pmc, irq, name, parent_name, rate);
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+void __init of_at91rm9200_clk_main_setup(struct device_node *np,
+					 struct at91_pmc *pmc)
+{
+	of_at91_clk_main_setup(np, pmc);
+}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index c8a9a63..9b4719b 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -231,6 +231,11 @@ out_free_pmc:
 }
 
 static const struct of_device_id pmc_clk_ids[] __initdata = {
+	/* Main clock */
+	{
+		.compatible = "atmel,at91rm9200-clk-main",
+		.data = of_at91rm9200_clk_main_setup
+	},
 	{ /*sentinel*/ }
 };
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index d92b946..729aa46 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -58,4 +58,7 @@ static inline void pmc_write(struct at91_pmc *pmc, int offset, u32 value)
 int of_at91_get_clk_range(struct device_node *np, const char *propname,
 			  struct clk_range *range);
 
+extern void __init of_at91rm9200_clk_main_setup(struct device_node *np,
+						struct at91_pmc *pmc);
+
 #endif /* __PMC_H_ */
-- 
1.7.9.5


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

* [PATCH v5 06/17] clk: at91: add PMC pll clocks
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (4 preceding siblings ...)
  2013-11-12 21:54 ` [PATCH v5 05/17] clk: at91: add PMC main clock Boris BREZILLON
@ 2013-11-12 21:57 ` Boris BREZILLON
  2013-11-27 21:48   ` Mike Turquette
  2013-11-12 22:00 ` [PATCH v5 07/17] clk: at91: add PMC master clock Boris BREZILLON
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 21:57 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds new at91 pll clock implementation using common clk framework.

The pll clock layout describe the PLLX register layout.
There are four pll clock layouts:
- at91rm9200
- at91sam9g20
- at91sam9g45
- sama5d3

PLL clocks are given characteristics:
- min/max clock source rate
- ranges of valid clock output rates
- values to set in out and icpll fields for each supported output range

These characteristics are checked during rate change to avoid
over/underclocking.

These characteristics are described in atmel's SoC datasheet in
"Electrical Characteristics" paragraph.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 drivers/clk/at91/Makefile     |    2 +-
 drivers/clk/at91/clk-pll.c    |  540 +++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/clk-plldiv.c |  137 +++++++++++
 drivers/clk/at91/pmc.c        |   21 ++
 drivers/clk/at91/pmc.h        |   11 +
 include/linux/clk/at91_pmc.h  |    2 +
 6 files changed, 712 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/at91/clk-pll.c
 create mode 100644 drivers/clk/at91/clk-plldiv.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 44105bd..902bbf1 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -3,4 +3,4 @@
 #
 
 obj-y += pmc.o
-obj-y += clk-main.o
+obj-y += clk-main.o clk-pll.o clk-plldiv.o
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
new file mode 100644
index 0000000..57cedb5
--- /dev/null
+++ b/drivers/clk/at91/clk-pll.c
@@ -0,0 +1,540 @@
+/*
+ * drivers/clk/at91/clk-pll.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include "pmc.h"
+
+#define PLL_STATUS_MASK(id)	(1 << (1 + (id)))
+#define PLL_REG(id)		(AT91_CKGR_PLLAR + ((id) * 4))
+#define PLL_DIV_MASK		0xff
+#define PLL_DIV_MAX		PLL_DIV_MASK
+#define PLL_DIV(reg)		((reg) & PLL_DIV_MASK)
+#define PLL_MUL(reg, layout)	(((reg) >> (layout)->mul_shift) & \
+				 (layout)->mul_mask)
+#define PLL_ICPR_SHIFT(id)	((id) * 16)
+#define PLL_ICPR_MASK(id)	(0xffff << PLL_ICPR_SHIFT(id))
+#define PLL_MAX_COUNT		0x3ff
+#define PLL_COUNT_SHIFT		8
+#define PLL_OUT_SHIFT		14
+#define PLL_MAX_ID		1
+
+struct clk_pll_characteristics {
+	struct clk_range input;
+	int num_output;
+	struct clk_range *output;
+	u16 *icpll;
+	u8 *out;
+};
+
+struct clk_pll_layout {
+	u32 pllr_mask;
+	u16 mul_mask;
+	u8 mul_shift;
+};
+
+#define to_clk_pll(hw) container_of(hw, struct clk_pll, hw)
+
+struct clk_pll {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+	unsigned int irq;
+	wait_queue_head_t wait;
+	u8 id;
+	u8 div;
+	u8 range;
+	u16 mul;
+	const struct clk_pll_layout *layout;
+	const struct clk_pll_characteristics *characteristics;
+};
+
+static irqreturn_t clk_pll_irq_handler(int irq, void *dev_id)
+{
+	struct clk_pll *pll = (struct clk_pll *)dev_id;
+
+	wake_up(&pll->wait);
+	disable_irq_nosync(pll->irq);
+
+	return IRQ_HANDLED;
+}
+
+static int clk_pll_prepare(struct clk_hw *hw)
+{
+	struct clk_pll *pll = to_clk_pll(hw);
+	struct at91_pmc *pmc = pll->pmc;
+	const struct clk_pll_layout *layout = pll->layout;
+	u8 id = pll->id;
+	u32 mask = PLL_STATUS_MASK(id);
+	int offset = PLL_REG(id);
+	u8 out = 0;
+	u32 pllr, icpr;
+	u8 div;
+	u16 mul;
+
+	pllr = pmc_read(pmc, offset);
+	div = PLL_DIV(pllr);
+	mul = PLL_MUL(pllr, layout);
+
+	if ((pmc_read(pmc, AT91_PMC_SR) & mask) &&
+	    (div == pll->div && mul == pll->mul))
+		return 0;
+
+	if (characteristics->out)
+		out = characteristics->out[pll->range];
+	if (characteristics->icpll) {
+		icpr = pmc_read(pmc, AT91_PMC_PLLICPR) & ~PLL_ICPR_MASK(id);
+		icpr |= (characteristics->icpll[pll->range] <<
+			PLL_ICPR_SHIFT(id));
+		pmc_write(pmc, AT91_PMC_PLLICPR, icpr);
+	}
+
+	pllr &= ~layout->pllr_mask;
+	pllr |= layout->pllr_mask &
+	       (pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) |
+		(out << PLL_OUT_SHIFT) |
+		((pll->mul & layout->mul_mask) << layout->mul_shift));
+	pmc_write(pmc, offset, pllr);
+
+	while (!(pmc_read(pmc, AT91_PMC_SR) & mask)) {
+		enable_irq(pll->irq);
+		wait_event(pll->wait,
+			   pmc_read(pmc, AT91_PMC_SR) & mask);
+	}
+
+	return 0;
+}
+
+static int clk_pll_is_ready(struct clk_hw *hw)
+{
+	struct clk_pll *pll = to_clk_pll(hw);
+	struct at91_pmc *pmc = pll->pmc;
+
+	return !!(pmc_read(pmc, AT91_PMC_SR) &
+		  PLL_STATUS_MASK(pll->id));
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+	struct clk_pll *pll = to_clk_pll(hw);
+	struct at91_pmc *pmc = pll->pmc;
+	const struct clk_pll_layout *layout = pll->layout;
+	int offset = PLL_REG(pll->id);
+	u32 tmp = pmc_read(pmc, offset) & ~(layout->pllr_mask);
+
+	pmc_write(pmc, offset, tmp);
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+					 unsigned long parent_rate)
+{
+	struct clk_pll *pll = to_clk_pll(hw);
+	const struct clk_pll_layout *layout = pll->layout;
+	struct at91_pmc *pmc = pll->pmc;
+	int offset = PLL_REG(pll->id);
+	u32 tmp = pmc_read(pmc, offset) & layout->pllr_mask;
+	u8 div = PLL_DIV(tmp);
+	u16 mul = PLL_MUL(tmp, layout);
+	if (!div || !mul)
+		return 0;
+
+	return (parent_rate * (mul + 1)) / div;
+}
+
+static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
+				     unsigned long parent_rate,
+				     u32 *div, u32 *mul,
+				     u32 *index) {
+	unsigned long maxrate;
+	unsigned long minrate;
+	unsigned long divrate;
+	unsigned long bestdiv = 1;
+	unsigned long bestmul;
+	unsigned long tmpdiv;
+	unsigned long roundup;
+	unsigned long rounddown;
+	unsigned long remainder;
+	unsigned long bestremainder;
+	unsigned long maxmul;
+	unsigned long maxdiv;
+	unsigned long mindiv;
+	int i = 0;
+	const struct clk_pll_layout *layout = pll->layout;
+	const struct clk_pll_characteristics *characteristics =
+							pll->characteristics;
+
+	/* Minimum divider = 1 */
+	/* Maximum multiplier = max_mul */
+	maxmul = layout->mul_mask + 1;
+	maxrate = (parent_rate * maxmul) / 1;
+
+	/* Maximum divider = max_div */
+	/* Minimum multiplier = 2 */
+	maxdiv = PLL_DIV_MAX;
+	minrate = (parent_rate * 2) / maxdiv;
+
+	if (parent_rate < characteristics->input.min ||
+	    parent_rate < characteristics->input.max)
+		return -ERANGE;
+
+	if (parent_rate < minrate || parent_rate > maxrate)
+		return -ERANGE;
+
+	for (i = 0; i < characteristics->num_output; i++) {
+		if (parent_rate >= characteristics->output[i].min &&
+		    parent_rate <= characteristics->output[i].max)
+			break;
+	}
+
+	if (i >= characteristics->num_output)
+		return -ERANGE;
+
+	bestmul = rate / parent_rate;
+	rounddown = parent_rate % rate;
+	roundup = rate - rounddown;
+	bestremainder = roundup < rounddown ? roundup : rounddown;
+
+	if (!bestremainder) {
+		if (div)
+			*div = bestdiv;
+		if (mul)
+			*mul = bestmul;
+		if (index)
+			*index = i;
+		return rate;
+	}
+
+	maxdiv = 255 / (bestmul + 1);
+	if (parent_rate / maxdiv < characteristics->input.min)
+		maxdiv = parent_rate / characteristics->input.min;
+	mindiv = parent_rate / characteristics->input.max;
+	if (parent_rate % characteristics->input.max)
+		mindiv++;
+
+	for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) {
+		divrate = parent_rate / tmpdiv;
+
+		rounddown = rate % divrate;
+		roundup = divrate - rounddown;
+		remainder = roundup < rounddown ? roundup : rounddown;
+
+		if (remainder < bestremainder) {
+			bestremainder = remainder;
+			bestmul = rate / divrate;
+			bestdiv = tmpdiv;
+		}
+
+		if (!remainder)
+			break;
+	}
+
+	rate = (parent_rate / bestdiv) * bestmul;
+
+	if (div)
+		*div = bestdiv;
+	if (mul)
+		*mul = bestmul;
+	if (index)
+		*index = i;
+
+	return rate;
+}
+
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+					unsigned long *parent_rate)
+{
+	struct clk_pll *pll = to_clk_pll(hw);
+
+	return clk_pll_get_best_div_mul(pll, rate, *parent_rate,
+					NULL, NULL, NULL);
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			    unsigned long parent_rate)
+{
+	struct clk_pll *pll = to_clk_pll(hw);
+	struct at91_pmc *pmc = pll->pmc;
+	const struct clk_pll_layout *layout = pll->layout;
+	const struct clk_pll_characteristics *characteristics =
+						pll->characteristics;
+	u8 id = pll->id;
+	int offset = PLL_REG(id);
+	long ret;
+	u32 div;
+	u32 mul;
+	u32 index;
+	u32 tmp;
+	u8 out = 0;
+
+	ret = clk_pll_get_best_div_mul(pll, rate, parent_rate,
+				       &div, &mul, &index);
+	if (ret < 0)
+		return ret;
+
+	pll->range = index;
+	pll->div = div;
+	pll->mul = mul;
+
+	return 0;
+}
+
+static const struct clk_ops pll_ops = {
+	.prepare = clk_pll_prepare,
+	.is_prepared = clk_pll_is_ready,
+	.disable = clk_pll_disable,
+	.is_enabled = clk_pll_is_ready,
+	.recalc_rate = clk_pll_recalc_rate,
+	.round_rate = clk_pll_round_rate,
+	.set_rate = clk_pll_set_rate,
+};
+
+static struct clk * __init
+at91_clk_register_pll(struct at91_pmc *pmc, unsigned int irq, const char *name,
+		      const char *parent_name, u8 id,
+		      const struct clk_pll_layout *layout,
+		      const struct clk_pll_characteristics *characteristics)
+{
+	struct clk_pll *pll;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+	int ret;
+	int offset = PLL_REG(id);
+	u32 tmp;
+
+	if (id > PLL_MAX_ID)
+		return ERR_PTR(-EINVAL);
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &pll_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE;
+
+	pll->id = id;
+	pll->hw.init = &init;
+	pll->layout = layout;
+	pll->characteristics = characteristics;
+	pll->pmc = pmc;
+	pll->irq = irq;
+	tmp = pmc_read(pmc, offset) & layout->pllr_mask;
+	pll->div = PLL_DIV(tmp);
+	pll->mul = PLL_MUL(tmp, layout);
+	init_waitqueue_head(&pll->wait);
+	irq_set_status_flags(pll->irq, IRQ_NOAUTOEN);
+	ret = request_irq(pll->irq, clk_pll_irq_handler, IRQF_TRIGGER_HIGH,
+			  id ? "clk-pllb" : "clk-plla", pll);
+	if (ret)
+		return ERR_PTR(ret);
+
+	clk = clk_register(NULL, &pll->hw);
+	if (IS_ERR(clk))
+		kfree(pll);
+
+	return clk;
+}
+
+
+static const struct clk_pll_layout at91rm9200_pll_layout = {
+	.pllr_mask = 0x7FFFFFF,
+	.mul_shift = 16,
+	.mul_mask = 0x7FF,
+};
+
+static const struct clk_pll_layout at91sam9g45_pll_layout = {
+	.pllr_mask = 0xFFFFFF,
+	.mul_shift = 16,
+	.mul_mask = 0xFF,
+};
+
+static const struct clk_pll_layout at91sam9g20_pllb_layout = {
+	.pllr_mask = 0x3FFFFF,
+	.mul_shift = 16,
+	.mul_mask = 0x3F,
+};
+
+static const struct clk_pll_layout sama5d3_pll_layout = {
+	.pllr_mask = 0x1FFFFFF,
+	.mul_shift = 18,
+	.mul_mask = 0x7F,
+};
+
+
+static struct clk_pll_characteristics * __init
+of_at91_clk_pll_get_characteristics(struct device_node *np)
+{
+	int i;
+	int offset;
+	u32 tmp;
+	int num_output;
+	u32 num_cells;
+	struct clk_range input;
+	struct clk_range *output;
+	u8 *out = NULL;
+	u16 *icpll = NULL;
+	struct clk_pll_characteristics *characteristics;
+
+	if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input))
+		return NULL;
+
+	if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells",
+				 &num_cells))
+		return NULL;
+
+	if (num_cells < 2 || num_cells > 4)
+		return NULL;
+
+	if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp))
+		return NULL;
+	num_output = tmp / (sizeof(u32) * num_cells);
+
+	characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
+	if (!characteristics)
+		return NULL;
+
+	output = kzalloc(sizeof(*output) * num_output, GFP_KERNEL);
+	if (!output)
+		goto out_free_characteristics;
+
+	if (num_cells > 2) {
+		out = kzalloc(sizeof(*out) * num_output, GFP_KERNEL);
+		if (!out)
+			goto out_free_output;
+	}
+
+	if (num_cells > 3) {
+		icpll = kzalloc(sizeof(*icpll) * num_output, GFP_KERNEL);
+		if (!icpll)
+			goto out_free_output;
+	}
+
+	for (i = 0; i < num_output; i++) {
+		offset = i * num_cells;
+		if (of_property_read_u32_index(np,
+					       "atmel,pll-clk-output-ranges",
+					       offset, &tmp))
+			goto out_free_output;
+		output[i].min = tmp;
+		if (of_property_read_u32_index(np,
+					       "atmel,pll-clk-output-ranges",
+					       offset + 1, &tmp))
+			goto out_free_output;
+		output[i].max = tmp;
+
+		if (num_cells == 2)
+			continue;
+
+		if (of_property_read_u32_index(np,
+					       "atmel,pll-clk-output-ranges",
+					       offset + 2, &tmp))
+			goto out_free_output;
+		out[i] = tmp;
+
+		if (num_cells == 3)
+			continue;
+
+		if (of_property_read_u32_index(np,
+					       "atmel,pll-clk-output-ranges",
+					       offset + 3, &tmp))
+			goto out_free_output;
+		icpll[i] = tmp;
+	}
+
+	characteristics->input = input;
+	characteristics->num_output = num_output;
+	characteristics->output = output;
+	characteristics->out = out;
+	characteristics->icpll = icpll;
+	return characteristics;
+
+out_free_output:
+	kfree(icpll);
+	kfree(out);
+	kfree(output);
+out_free_characteristics:
+	kfree(characteristics);
+	return NULL;
+}
+
+static void __init
+of_at91_clk_pll_setup(struct device_node *np, struct at91_pmc *pmc,
+		      const struct clk_pll_layout *layout)
+{
+	u32 id;
+	unsigned int irq;
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+	struct clk_pll_characteristics *characteristics;
+
+	if (of_property_read_u32(np, "reg", &id))
+		return;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	characteristics = of_at91_clk_pll_get_characteristics(np);
+	if (!characteristics)
+		return;
+
+	irq = irq_of_parse_and_map(np, 0);
+	if (!irq)
+		return;
+
+	clk = at91_clk_register_pll(pmc, irq, name, parent_name, id, layout,
+				    characteristics);
+	if (IS_ERR(clk))
+		goto out_free_characteristics;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+	return;
+
+out_free_characteristics:
+	kfree(characteristics);
+}
+
+void __init of_at91rm9200_clk_pll_setup(struct device_node *np,
+					       struct at91_pmc *pmc)
+{
+	of_at91_clk_pll_setup(np, pmc, &at91rm9200_pll_layout);
+}
+
+void __init of_at91sam9g45_clk_pll_setup(struct device_node *np,
+						struct at91_pmc *pmc)
+{
+	of_at91_clk_pll_setup(np, pmc, &at91sam9g45_pll_layout);
+}
+
+void __init of_at91sam9g20_clk_pllb_setup(struct device_node *np,
+						 struct at91_pmc *pmc)
+{
+	of_at91_clk_pll_setup(np, pmc, &at91sam9g20_pllb_layout);
+}
+
+void __init of_sama5d3_clk_pll_setup(struct device_node *np,
+					    struct at91_pmc *pmc)
+{
+	of_at91_clk_pll_setup(np, pmc, &sama5d3_pll_layout);
+}
diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c
new file mode 100644
index 0000000..2bbb22b
--- /dev/null
+++ b/drivers/clk/at91/clk-plldiv.c
@@ -0,0 +1,137 @@
+/*
+ * drivers/clk/at91/clk-plldiv.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+
+#include "pmc.h"
+
+#define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw)
+
+struct clk_plldiv {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+};
+
+static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw,
+					    unsigned long parent_rate)
+{
+	struct clk_plldiv *plldiv = to_clk_plldiv(hw);
+	struct at91_pmc *pmc = plldiv->pmc;
+
+	if (pmc_read(pmc, AT91_PMC_MCKR) & AT91_PMC_PLLADIV2)
+		return parent_rate / 2;
+
+	return parent_rate;
+}
+
+static long clk_plldiv_round_rate(struct clk_hw *hw, unsigned long rate,
+					unsigned long *parent_rate)
+{
+	unsigned long div;
+
+	if (rate > *parent_rate)
+		return *parent_rate;
+	div = *parent_rate / 2;
+	if (rate < div)
+		return div;
+
+	if (rate - div < *parent_rate - rate)
+		return div;
+
+	return *parent_rate;
+}
+
+static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long parent_rate)
+{
+	struct clk_plldiv *plldiv = to_clk_plldiv(hw);
+	struct at91_pmc *pmc = plldiv->pmc;
+	u32 tmp;
+
+	if (parent_rate != rate && (parent_rate / 2) != rate)
+		return -EINVAL;
+
+	pmc_lock(pmc);
+	tmp = pmc_read(pmc, AT91_PMC_MCKR) & ~AT91_PMC_PLLADIV2;
+	if ((parent_rate / 2) == rate)
+		tmp |= AT91_PMC_PLLADIV2;
+	pmc_write(pmc, AT91_PMC_MCKR, tmp);
+	pmc_unlock(pmc);
+
+	return 0;
+}
+
+static const struct clk_ops plldiv_ops = {
+	.recalc_rate = clk_plldiv_recalc_rate,
+	.round_rate = clk_plldiv_round_rate,
+	.set_rate = clk_plldiv_set_rate,
+};
+
+static struct clk * __init
+at91_clk_register_plldiv(struct at91_pmc *pmc, const char *name,
+			 const char *parent_name)
+{
+	struct clk_plldiv *plldiv;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL);
+	if (!plldiv)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &plldiv_ops;
+	init.parent_names = parent_name ? &parent_name : NULL;
+	init.num_parents = parent_name ? 1 : 0;
+	init.flags = CLK_SET_RATE_GATE;
+
+	plldiv->hw.init = &init;
+	plldiv->pmc = pmc;
+
+	clk = clk_register(NULL, &plldiv->hw);
+
+	if (IS_ERR(clk))
+		kfree(plldiv);
+
+	return clk;
+}
+
+static void __init
+of_at91_clk_plldiv_setup(struct device_node *np, struct at91_pmc *pmc)
+{
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	clk = at91_clk_register_plldiv(pmc, name, parent_name);
+
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+	return;
+}
+
+void __init of_at91sam9x5_clk_plldiv_setup(struct device_node *np,
+					   struct at91_pmc *pmc)
+{
+	of_at91_clk_plldiv_setup(np, pmc);
+}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 9b4719b..d26fdb7 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -236,6 +236,27 @@ static const struct of_device_id pmc_clk_ids[] __initdata = {
 		.compatible = "atmel,at91rm9200-clk-main",
 		.data = of_at91rm9200_clk_main_setup
 	},
+	/* PLL clocks */
+	{
+		.compatible = "atmel,at91rm9200-clk-pll",
+		.data = of_at91rm9200_clk_pll_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9g45-clk-pll",
+		.data = of_at91sam9g45_clk_pll_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9g20-clk-pllb",
+		.data = of_at91sam9g20_clk_pllb_setup,
+	},
+	{
+		.compatible = "atmel,sama5d3-clk-pll",
+		.data = of_sama5d3_clk_pll_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-clk-plldiv",
+		.data = of_at91sam9x5_clk_plldiv_setup,
+	},
 	{ /*sentinel*/ }
 };
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 729aa46..15aaf38 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -61,4 +61,15 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname,
 extern void __init of_at91rm9200_clk_main_setup(struct device_node *np,
 						struct at91_pmc *pmc);
 
+extern void __init of_at91rm9200_clk_pll_setup(struct device_node *np,
+					       struct at91_pmc *pmc);
+extern void __init of_at91sam9g45_clk_pll_setup(struct device_node *np,
+						struct at91_pmc *pmc);
+extern void __init of_at91sam9g20_clk_pllb_setup(struct device_node *np,
+						 struct at91_pmc *pmc);
+extern void __init of_sama5d3_clk_pll_setup(struct device_node *np,
+					    struct at91_pmc *pmc);
+extern void __init of_at91sam9x5_clk_plldiv_setup(struct device_node *np,
+						  struct at91_pmc *pmc);
+
 #endif /* __PMC_H_ */
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index 00c45b3..a6911eb 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -164,6 +164,8 @@ extern void __iomem *at91_pmc_base;
 #define		AT91_PMC_CFDEV		(1 << 18)		/* Clock Failure Detector Event [some SAM9] */
 #define	AT91_PMC_IMR		0x6c			/* Interrupt Mask Register */
 
+#define AT91_PMC_PLLICPR	0x80			/* PLL Charge Pump Current Register */
+
 #define AT91_PMC_PROT		0xe4			/* Write Protect Mode Register [some SAM9] */
 #define		AT91_PMC_WPEN		(0x1  <<  0)		/* Write Protect Enable */
 #define		AT91_PMC_WPKEY		(0xffffff << 8)		/* Write Protect Key */
-- 
1.7.9.5


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

* [PATCH v5 07/17] clk: at91: add PMC master clock
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (5 preceding siblings ...)
  2013-11-12 21:57 ` [PATCH v5 06/17] clk: at91: add PMC pll clocks Boris BREZILLON
@ 2013-11-12 22:00 ` Boris BREZILLON
  2013-11-12 22:05 ` [PATCH v5 08/17] clk: at91: add PMC system clocks Boris BREZILLON
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 22:00 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds new at91 master clock implementation using common clk
framework.

The master clock layout describe the MCKR register layout.
There are 2 master clock layouts:
- at91rm9200
- at91sam9x5

Master clocks are given characteristics:
- min/max clock output rate

These characteristics are checked during rate change to avoid
over/underclocking.

These characteristics are described in atmel's SoC datasheet in
"Electrical Characteristics" paragraph.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 drivers/clk/at91/Makefile     |    2 +-
 drivers/clk/at91/clk-master.c |  272 +++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/clk-pll.c    |   10 +-
 drivers/clk/at91/pmc.c        |    9 ++
 drivers/clk/at91/pmc.h        |    5 +
 5 files changed, 289 insertions(+), 9 deletions(-)
 create mode 100644 drivers/clk/at91/clk-master.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 902bbf1..e28fb2b 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -3,4 +3,4 @@
 #
 
 obj-y += pmc.o
-obj-y += clk-main.o clk-pll.o clk-plldiv.o
+obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
new file mode 100644
index 0000000..65b81de
--- /dev/null
+++ b/drivers/clk/at91/clk-master.c
@@ -0,0 +1,272 @@
+/*
+ * drivers/clk/at91/clk-master.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include "pmc.h"
+
+#define MASTER_SOURCE_MAX	4
+
+#define MASTER_PRES_MASK	0x7
+#define MASTER_PRES_MAX		MASTER_PRES_MASK
+#define MASTER_DIV_SHIFT	8
+#define MASTER_DIV_MASK		0x3
+
+struct clk_master_characteristics {
+	struct clk_range output;
+	u32 divisors[4];
+	u8 have_div3_pres;
+};
+
+struct clk_master_layout {
+	u32 mask;
+	u8 pres_shift;
+};
+
+#define to_clk_master(hw) container_of(hw, struct clk_master, hw)
+
+struct clk_master {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+	unsigned int irq;
+	wait_queue_head_t wait;
+	const struct clk_master_layout *layout;
+	const struct clk_master_characteristics *characteristics;
+};
+
+static irqreturn_t clk_master_irq_handler(int irq, void *dev_id)
+{
+	struct clk_master *master = (struct clk_master *)dev_id;
+
+	wake_up(&master->wait);
+	disable_irq_nosync(master->irq);
+
+	return IRQ_HANDLED;
+}
+static int clk_master_prepare(struct clk_hw *hw)
+{
+	struct clk_master *master = to_clk_master(hw);
+	struct at91_pmc *pmc = master->pmc;
+
+	while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY)) {
+		enable_irq(master->irq);
+		wait_event(master->wait,
+			   pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY);
+	}
+
+	return 0;
+}
+
+static int clk_master_is_prepared(struct clk_hw *hw)
+{
+	struct clk_master *master = to_clk_master(hw);
+
+	return !!(pmc_read(master->pmc, AT91_PMC_SR) & AT91_PMC_MCKRDY);
+}
+
+static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
+					    unsigned long parent_rate)
+{
+	u8 pres;
+	u8 div;
+	unsigned long rate = parent_rate;
+	struct clk_master *master = to_clk_master(hw);
+	struct at91_pmc *pmc = master->pmc;
+	const struct clk_master_layout *layout = master->layout;
+	const struct clk_master_characteristics *characteristics =
+						master->characteristics;
+	u32 tmp;
+
+	pmc_lock(pmc);
+	tmp = pmc_read(pmc, AT91_PMC_MCKR) & layout->mask;
+	pmc_unlock(pmc);
+
+	pres = (tmp >> layout->pres_shift) & MASTER_PRES_MASK;
+	div = (tmp >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+
+	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
+		rate /= 3;
+	else
+		rate >>= pres;
+
+	rate /= characteristics->divisors[div];
+
+	if (rate < characteristics->output.min)
+		pr_warn("master clk is underclocked");
+	else if (rate > characteristics->output.max)
+		pr_warn("master clk is overclocked");
+
+	return rate;
+}
+
+static u8 clk_master_get_parent(struct clk_hw *hw)
+{
+	struct clk_master *master = to_clk_master(hw);
+	struct at91_pmc *pmc = master->pmc;
+
+	return pmc_read(pmc, AT91_PMC_MCKR) & AT91_PMC_CSS;
+}
+
+static const struct clk_ops master_ops = {
+	.prepare = clk_master_prepare,
+	.is_prepared = clk_master_is_prepared,
+	.recalc_rate = clk_master_recalc_rate,
+	.get_parent = clk_master_get_parent,
+};
+
+static struct clk * __init
+at91_clk_register_master(struct at91_pmc *pmc, unsigned int irq,
+		const char *name, int num_parents,
+		const char **parent_names,
+		const struct clk_master_layout *layout,
+		const struct clk_master_characteristics *characteristics)
+{
+	int ret;
+	struct clk_master *master;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	if (!pmc || !irq || !name || !num_parents || !parent_names)
+		return ERR_PTR(-EINVAL);
+
+	master = kzalloc(sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &master_ops;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	init.flags = 0;
+
+	master->hw.init = &init;
+	master->layout = layout;
+	master->characteristics = characteristics;
+	master->pmc = pmc;
+	master->irq = irq;
+	init_waitqueue_head(&master->wait);
+	irq_set_status_flags(master->irq, IRQ_NOAUTOEN);
+	ret = request_irq(master->irq, clk_master_irq_handler,
+			  IRQF_TRIGGER_HIGH, "clk-master", master);
+	if (ret)
+		return ERR_PTR(ret);
+
+	clk = clk_register(NULL, &master->hw);
+	if (IS_ERR(clk))
+		kfree(master);
+
+	return clk;
+}
+
+
+static const struct clk_master_layout at91rm9200_master_layout = {
+	.mask = 0x31F,
+	.pres_shift = 2,
+};
+
+static const struct clk_master_layout at91sam9x5_master_layout = {
+	.mask = 0x373,
+	.pres_shift = 4,
+};
+
+
+static struct clk_master_characteristics * __init
+of_at91_clk_master_get_characteristics(struct device_node *np)
+{
+	struct clk_master_characteristics *characteristics;
+
+	characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
+	if (!characteristics)
+		return NULL;
+
+	if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output))
+		goto out_free_characteristics;
+
+	of_property_read_u32_array(np, "atmel,clk-divisors",
+				   characteristics->divisors, 4);
+
+	characteristics->have_div3_pres =
+		of_property_read_bool(np, "atmel,master-clk-have-div3-pres");
+
+	return characteristics;
+
+out_free_characteristics:
+	kfree(characteristics);
+	return NULL;
+}
+
+static void __init
+of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc,
+			 const struct clk_master_layout *layout)
+{
+	struct clk *clk;
+	int num_parents;
+	int i;
+	unsigned int irq;
+	const char *parent_names[MASTER_SOURCE_MAX];
+	const char *name = np->name;
+	struct clk_master_characteristics *characteristics;
+
+	num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
+	if (num_parents <= 0 || num_parents > MASTER_SOURCE_MAX)
+		return;
+
+	for (i = 0; i < num_parents; ++i) {
+		parent_names[i] = of_clk_get_parent_name(np, i);
+		if (!parent_names[i])
+			return;
+	}
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	characteristics = of_at91_clk_master_get_characteristics(np);
+	if (!characteristics)
+		return;
+
+	irq = irq_of_parse_and_map(np, 0);
+	if (!irq)
+		return;
+
+	clk = at91_clk_register_master(pmc, irq, name, num_parents,
+				       parent_names, layout,
+				       characteristics);
+	if (IS_ERR(clk))
+		goto out_free_characteristics;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+	return;
+
+out_free_characteristics:
+	kfree(characteristics);
+}
+
+void __init of_at91rm9200_clk_master_setup(struct device_node *np,
+					   struct at91_pmc *pmc)
+{
+	of_at91_clk_master_setup(np, pmc, &at91rm9200_master_layout);
+}
+
+void __init of_at91sam9x5_clk_master_setup(struct device_node *np,
+					   struct at91_pmc *pmc)
+{
+	of_at91_clk_master_setup(np, pmc, &at91sam9x5_master_layout);
+}
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index 57cedb5..c4d0dc8 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -82,6 +82,8 @@ static int clk_pll_prepare(struct clk_hw *hw)
 	struct clk_pll *pll = to_clk_pll(hw);
 	struct at91_pmc *pmc = pll->pmc;
 	const struct clk_pll_layout *layout = pll->layout;
+	const struct clk_pll_characteristics *characteristics =
+							pll->characteristics;
 	u8 id = pll->id;
 	u32 mask = PLL_STATUS_MASK(id);
 	int offset = PLL_REG(id);
@@ -271,18 +273,10 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 			    unsigned long parent_rate)
 {
 	struct clk_pll *pll = to_clk_pll(hw);
-	struct at91_pmc *pmc = pll->pmc;
-	const struct clk_pll_layout *layout = pll->layout;
-	const struct clk_pll_characteristics *characteristics =
-						pll->characteristics;
-	u8 id = pll->id;
-	int offset = PLL_REG(id);
 	long ret;
 	u32 div;
 	u32 mul;
 	u32 index;
-	u32 tmp;
-	u8 out = 0;
 
 	ret = clk_pll_get_best_div_mul(pll, rate, parent_rate,
 				       &div, &mul, &index);
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index d26fdb7..d9975e7 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -257,6 +257,15 @@ static const struct of_device_id pmc_clk_ids[] __initdata = {
 		.compatible = "atmel,at91sam9x5-clk-plldiv",
 		.data = of_at91sam9x5_clk_plldiv_setup,
 	},
+	/* Master clock */
+	{
+		.compatible = "atmel,at91rm9200-clk-master",
+		.data = of_at91rm9200_clk_master_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-clk-master",
+		.data = of_at91sam9x5_clk_master_setup,
+	},
 	{ /*sentinel*/ }
 };
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 15aaf38..be5c964 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -72,4 +72,9 @@ extern void __init of_sama5d3_clk_pll_setup(struct device_node *np,
 extern void __init of_at91sam9x5_clk_plldiv_setup(struct device_node *np,
 						  struct at91_pmc *pmc);
 
+extern void __init of_at91rm9200_clk_master_setup(struct device_node *np,
+						  struct at91_pmc *pmc);
+extern void __init of_at91sam9x5_clk_master_setup(struct device_node *np,
+						  struct at91_pmc *pmc);
+
 #endif /* __PMC_H_ */
-- 
1.7.9.5


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

* [PATCH v5 08/17] clk: at91: add PMC system clocks
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (6 preceding siblings ...)
  2013-11-12 22:00 ` [PATCH v5 07/17] clk: at91: add PMC master clock Boris BREZILLON
@ 2013-11-12 22:05 ` Boris BREZILLON
  2013-11-27 22:05   ` Mike Turquette
  2013-11-12 22:08 ` [PATCH v5 09/17] clk: at91: add PMC peripheral clocks Boris BREZILLON
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 22:05 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds new at91 system clock implementation using common clk
framework.

Some peripherals need to enable a "system" clock in order to work properly.
Each system clock is given an id based on the bit position in SCER/SCDR
registers.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 drivers/clk/at91/Makefile     |    1 +
 drivers/clk/at91/clk-system.c |  137 +++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.c        |    5 ++
 drivers/clk/at91/pmc.h        |    3 +
 4 files changed, 146 insertions(+)
 create mode 100644 drivers/clk/at91/clk-system.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index e28fb2b..c2b7068 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -4,3 +4,4 @@
 
 obj-y += pmc.o
 obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o
+obj-y += clk-system.o
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
new file mode 100644
index 0000000..1c086f4
--- /dev/null
+++ b/drivers/clk/at91/clk-system.c
@@ -0,0 +1,137 @@
+/*
+ * drivers/clk/at91/clk-system.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+
+#include "pmc.h"
+
+#define SYSTEM_MAX_ID		31
+
+#define SYSTEM_MAX_NAME_SZ	32
+
+#define to_clk_system(hw) container_of(hw, struct clk_system, hw)
+struct clk_system {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+	u8 id;
+};
+
+static int clk_system_enable(struct clk_hw *hw)
+{
+	struct clk_system *sys = to_clk_system(hw);
+	struct at91_pmc *pmc = sys->pmc;
+
+	pmc_write(pmc, AT91_PMC_SCER, 1 << sys->id);
+	return 0;
+}
+
+static void clk_system_disable(struct clk_hw *hw)
+{
+	struct clk_system *sys = to_clk_system(hw);
+	struct at91_pmc *pmc = sys->pmc;
+
+	pmc_write(pmc, AT91_PMC_SCDR, 1 << sys->id);
+}
+
+static int clk_system_is_enabled(struct clk_hw *hw)
+{
+	struct clk_system *sys = to_clk_system(hw);
+	struct at91_pmc *pmc = sys->pmc;
+
+	return !!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id));
+}
+
+static const struct clk_ops system_ops = {
+	.enable = clk_system_enable,
+	.disable = clk_system_disable,
+	.is_enabled = clk_system_is_enabled,
+};
+
+static struct clk * __init
+at91_clk_register_system(struct at91_pmc *pmc, const char *name,
+			 const char *parent_name, u8 id)
+{
+	struct clk_system *sys;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	if (!parent_name || id > SYSTEM_MAX_ID)
+		return ERR_PTR(-EINVAL);
+
+	sys = kzalloc(sizeof(*sys), GFP_KERNEL);
+	if (!sys)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &system_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	/*
+	 * CLK_IGNORE_UNUSED is used to avoid ddrck switch off.
+	 * TODO : we should implement a driver supporting at91 ddr controller
+	 * (see drivers/memory) which would request and enable the ddrck clock.
+	 * When this is done we will be able to remove CLK_IGNORE_UNUSED flag.
+	 */
+	init.flags = CLK_IGNORE_UNUSED;
+
+	sys->id = id;
+	sys->hw.init = &init;
+	sys->pmc = pmc;
+
+	clk = clk_register(NULL, &sys->hw);
+	if (IS_ERR(clk))
+		kfree(sys);
+
+	return clk;
+}
+
+static void __init
+of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc)
+{
+	int num;
+	u32 id;
+	struct clk *clk;
+	const char *name;
+	struct device_node *sysclknp;
+	const char *parent_name;
+
+	num = of_get_child_count(np);
+	if (num > (SYSTEM_MAX_ID + 1))
+		return;
+
+	for_each_child_of_node(np, sysclknp) {
+		if (of_property_read_u32(sysclknp, "reg", &id))
+			continue;
+
+		if (of_property_read_string(np, "clock-output-names", &name))
+			name = sysclknp->name;
+
+		parent_name = of_clk_get_parent_name(sysclknp, 0);
+
+		clk = at91_clk_register_system(pmc, name, parent_name, id);
+		if (IS_ERR(clk))
+			continue;
+
+		of_clk_add_provider(sysclknp, of_clk_src_simple_get, clk);
+	}
+}
+
+void __init of_at91rm9200_clk_sys_setup(struct device_node *np,
+					struct at91_pmc *pmc)
+{
+	of_at91_clk_sys_setup(np, pmc);
+}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index d9975e7..6adc5be 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -266,6 +266,11 @@ static const struct of_device_id pmc_clk_ids[] __initdata = {
 		.compatible = "atmel,at91sam9x5-clk-master",
 		.data = of_at91sam9x5_clk_master_setup,
 	},
+	/* System clocks */
+	{
+		.compatible = "atmel,at91rm9200-clk-system",
+		.data = of_at91rm9200_clk_sys_setup,
+	},
 	{ /*sentinel*/ }
 };
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index be5c964..9c8a397 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -77,4 +77,7 @@ extern void __init of_at91rm9200_clk_master_setup(struct device_node *np,
 extern void __init of_at91sam9x5_clk_master_setup(struct device_node *np,
 						  struct at91_pmc *pmc);
 
+extern void __init of_at91rm9200_clk_sys_setup(struct device_node *np,
+					       struct at91_pmc *pmc);
+
 #endif /* __PMC_H_ */
-- 
1.7.9.5


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

* [PATCH v5 09/17] clk: at91: add PMC peripheral clocks
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (7 preceding siblings ...)
  2013-11-12 22:05 ` [PATCH v5 08/17] clk: at91: add PMC system clocks Boris BREZILLON
@ 2013-11-12 22:08 ` Boris BREZILLON
  2013-11-13  6:10 ` [PATCH v5 10/17] clk: at91: add peripheral clk macros for peripheral clk dt bindings Boris BREZILLON
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-12 22:08 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds new at91 peripheral clock implementation using common clk
framework.

Almost all peripherals provided by at91 SoCs need a clock to work properly.
This clock is enabled/disabled using PCER/PCDR resgisters.

Each peripheral is given an id (see atmel's datasheets) which is used to
define and reference peripheral clocks.

Some new SoCs (at91sam9x5 and sama5d3) provide a new register (PCR) where you
can configure the peripheral clock as a division of the master clock.
This will help reducing the peripherals power comsumption.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 drivers/clk/at91/Makefile         |    2 +-
 drivers/clk/at91/clk-peripheral.c |  412 +++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.c            |    9 +
 drivers/clk/at91/pmc.h            |    5 +
 4 files changed, 427 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/at91/clk-peripheral.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index c2b7068..04deba3 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -4,4 +4,4 @@
 
 obj-y += pmc.o
 obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o
-obj-y += clk-system.o
+obj-y += clk-system.o clk-peripheral.o
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
new file mode 100644
index 0000000..15c9518
--- /dev/null
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -0,0 +1,412 @@
+/*
+ * drivers/clk/at91/clk-peripheral.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+
+#include "pmc.h"
+
+#define PERIPHERAL_MAX		64
+
+#define PERIPHERAL_AT91RM9200	0
+#define PERIPHERAL_AT91SAM9X5	1
+
+#define PERIPHERAL_ID_MIN	2
+#define PERIPHERAL_ID_MAX	31
+#define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
+
+#define PERIPHERAL_RSHIFT_MASK	0x3
+#define PERIPHERAL_RSHIFT(val)	(((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
+
+#define PERIPHERAL_MAX_SHIFT	4
+
+struct clk_peripheral {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+	u32 id;
+};
+
+#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
+
+struct clk_sam9x5_peripheral {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+	struct clk_range range;
+	u32 id;
+	u32 div;
+	bool auto_div;
+};
+
+#define to_clk_sam9x5_peripheral(hw) \
+	container_of(hw, struct clk_sam9x5_peripheral, hw)
+
+static int clk_peripheral_enable(struct clk_hw *hw)
+{
+	struct clk_peripheral *periph = to_clk_peripheral(hw);
+	struct at91_pmc *pmc = periph->pmc;
+	int offset = AT91_PMC_PCER;
+	u32 id = periph->id;
+
+	if (id < PERIPHERAL_ID_MIN)
+		return 0;
+	if (id > PERIPHERAL_ID_MAX)
+		offset = AT91_PMC_PCER1;
+	pmc_write(pmc, offset, PERIPHERAL_MASK(id));
+	return 0;
+}
+
+static void clk_peripheral_disable(struct clk_hw *hw)
+{
+	struct clk_peripheral *periph = to_clk_peripheral(hw);
+	struct at91_pmc *pmc = periph->pmc;
+	int offset = AT91_PMC_PCDR;
+	u32 id = periph->id;
+
+	if (id < PERIPHERAL_ID_MIN)
+		return;
+	if (id > PERIPHERAL_ID_MAX)
+		offset = AT91_PMC_PCDR1;
+	pmc_write(pmc, offset, PERIPHERAL_MASK(id));
+}
+
+static int clk_peripheral_is_enabled(struct clk_hw *hw)
+{
+	struct clk_peripheral *periph = to_clk_peripheral(hw);
+	struct at91_pmc *pmc = periph->pmc;
+	int offset = AT91_PMC_PCSR;
+	u32 id = periph->id;
+
+	if (id < PERIPHERAL_ID_MIN)
+		return 1;
+	if (id > PERIPHERAL_ID_MAX)
+		offset = AT91_PMC_PCSR1;
+	return !!(pmc_read(pmc, offset) & PERIPHERAL_MASK(id));
+}
+
+static const struct clk_ops peripheral_ops = {
+	.enable = clk_peripheral_enable,
+	.disable = clk_peripheral_disable,
+	.is_enabled = clk_peripheral_is_enabled,
+};
+
+static struct clk * __init
+at91_clk_register_peripheral(struct at91_pmc *pmc, const char *name,
+			     const char *parent_name, u32 id)
+{
+	struct clk_peripheral *periph;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	if (!pmc || !name || !parent_name || id > PERIPHERAL_ID_MAX)
+		return ERR_PTR(-EINVAL);
+
+	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+	if (!periph)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &peripheral_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+	init.flags = 0;
+
+	periph->id = id;
+	periph->hw.init = &init;
+	periph->pmc = pmc;
+
+	clk = clk_register(NULL, &periph->hw);
+	if (IS_ERR(clk))
+		kfree(periph);
+
+	return clk;
+}
+
+static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
+{
+	struct clk *parent;
+	unsigned long parent_rate;
+	int shift = 0;
+
+	if (!periph->auto_div)
+		return;
+
+	if (periph->range.max) {
+		parent = clk_get_parent_by_index(periph->hw.clk, 0);
+		parent_rate = __clk_get_rate(parent);
+		if (!parent_rate)
+			return;
+
+		for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+			if (parent_rate >> shift <= periph->range.max)
+				break;
+		}
+	}
+
+	periph->auto_div = false;
+	periph->div = shift;
+}
+
+static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+	struct at91_pmc *pmc = periph->pmc;
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return 0;
+
+	pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) |
+				     AT91_PMC_PCR_CMD |
+				     AT91_PMC_PCR_DIV(periph->div) |
+				     AT91_PMC_PCR_EN);
+	return 0;
+}
+
+static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+	struct at91_pmc *pmc = periph->pmc;
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return;
+
+	pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) |
+				     AT91_PMC_PCR_CMD);
+}
+
+static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+	struct at91_pmc *pmc = periph->pmc;
+	int ret;
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return 1;
+
+	pmc_lock(pmc);
+	pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID));
+	ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_EN);
+	pmc_unlock(pmc);
+
+	return ret;
+}
+
+static unsigned long
+clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
+				  unsigned long parent_rate)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+	struct at91_pmc *pmc = periph->pmc;
+	u32 tmp;
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return parent_rate;
+
+	pmc_lock(pmc);
+	pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID));
+	tmp = pmc_read(pmc, AT91_PMC_PCR);
+	pmc_unlock(pmc);
+
+	if (tmp & AT91_PMC_PCR_EN) {
+		periph->div = PERIPHERAL_RSHIFT(tmp);
+		periph->auto_div = false;
+	} else {
+		clk_sam9x5_peripheral_autodiv(periph);
+	}
+
+	return parent_rate >> periph->div;
+}
+
+static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
+					     unsigned long rate,
+					     unsigned long *parent_rate)
+{
+	int shift = 0;
+	unsigned long best_rate;
+	unsigned long best_diff;
+	unsigned long cur_rate = *parent_rate;
+	unsigned long cur_diff;
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+
+	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
+		return *parent_rate;
+
+	if (periph->range.max) {
+		for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+			cur_rate = *parent_rate >> shift;
+			if (cur_rate <= periph->range.max)
+				break;
+		}
+	}
+
+	if (rate >= cur_rate)
+		return cur_rate;
+
+	best_diff = cur_rate - rate;
+	best_rate = cur_rate;
+	for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+		cur_rate = *parent_rate >> shift;
+		if (cur_rate < rate)
+			cur_diff = rate - cur_rate;
+		else
+			cur_diff = cur_rate - rate;
+
+		if (cur_diff < best_diff) {
+			best_diff = cur_diff;
+			best_rate = cur_rate;
+		}
+
+		if (!best_diff || cur_rate < rate)
+			break;
+	}
+
+	return best_rate;
+}
+
+static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
+					  unsigned long rate,
+					  unsigned long parent_rate)
+{
+	int shift;
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
+		if (parent_rate == rate)
+			return 0;
+		else
+			return -EINVAL;
+	}
+
+	if (periph->range.max && rate > periph->range.max)
+		return -EINVAL;
+
+	for (shift = 0; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+		if (parent_rate >> shift == rate) {
+			periph->auto_div = false;
+			periph->div = shift;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static const struct clk_ops sam9x5_peripheral_ops = {
+	.enable = clk_sam9x5_peripheral_enable,
+	.disable = clk_sam9x5_peripheral_disable,
+	.is_enabled = clk_sam9x5_peripheral_is_enabled,
+	.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
+	.round_rate = clk_sam9x5_peripheral_round_rate,
+	.set_rate = clk_sam9x5_peripheral_set_rate,
+};
+
+static struct clk * __init
+at91_clk_register_sam9x5_peripheral(struct at91_pmc *pmc, const char *name,
+				    const char *parent_name, u32 id,
+				    const struct clk_range *range)
+{
+	struct clk_sam9x5_peripheral *periph;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	if (!pmc || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+	if (!periph)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &sam9x5_peripheral_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+	init.flags = 0;
+
+	periph->id = id;
+	periph->hw.init = &init;
+	periph->div = 0;
+	periph->pmc = pmc;
+	periph->auto_div = true;
+	periph->range = *range;
+
+	clk = clk_register(NULL, &periph->hw);
+	if (IS_ERR(clk))
+		kfree(periph);
+	else
+		clk_sam9x5_peripheral_autodiv(periph);
+
+	return clk;
+}
+
+static void __init
+of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type)
+{
+	int num;
+	u32 id;
+	struct clk *clk;
+	const char *parent_name;
+	const char *name;
+	struct device_node *periphclknp;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	if (!parent_name)
+		return;
+
+	num = of_get_child_count(np);
+	if (!num || num > PERIPHERAL_MAX)
+		return;
+
+	for_each_child_of_node(np, periphclknp) {
+		if (of_property_read_u32(periphclknp, "reg", &id))
+			continue;
+
+		if (id >= PERIPHERAL_MAX)
+			continue;
+
+		if (of_property_read_string(np, "clock-output-names", &name))
+			name = periphclknp->name;
+
+		if (type == PERIPHERAL_AT91RM9200) {
+			clk = at91_clk_register_peripheral(pmc, name,
+							   parent_name, id);
+		} else {
+			struct clk_range range = CLK_RANGE(0, 0);
+
+			of_at91_get_clk_range(periphclknp,
+					      "atmel,clk-output-range",
+					      &range);
+
+			clk = at91_clk_register_sam9x5_peripheral(pmc, name,
+								  parent_name,
+								  id, &range);
+		}
+
+		if (IS_ERR(clk))
+			continue;
+
+		of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
+	}
+}
+
+void __init of_at91rm9200_clk_periph_setup(struct device_node *np,
+					   struct at91_pmc *pmc)
+{
+	of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91RM9200);
+}
+
+void __init of_at91sam9x5_clk_periph_setup(struct device_node *np,
+					   struct at91_pmc *pmc)
+{
+	of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91SAM9X5);
+}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 6adc5be..09dfa63 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -271,6 +271,15 @@ static const struct of_device_id pmc_clk_ids[] __initdata = {
 		.compatible = "atmel,at91rm9200-clk-system",
 		.data = of_at91rm9200_clk_sys_setup,
 	},
+	/* Peripheral clocks */
+	{
+		.compatible = "atmel,at91rm9200-clk-peripheral",
+		.data = of_at91rm9200_clk_periph_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-clk-peripheral",
+		.data = of_at91sam9x5_clk_periph_setup,
+	},
 	{ /*sentinel*/ }
 };
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 9c8a397..a09212b 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -80,4 +80,9 @@ extern void __init of_at91sam9x5_clk_master_setup(struct device_node *np,
 extern void __init of_at91rm9200_clk_sys_setup(struct device_node *np,
 					       struct at91_pmc *pmc);
 
+extern void __init of_at91rm9200_clk_periph_setup(struct device_node *np,
+						  struct at91_pmc *pmc);
+extern void __init of_at91sam9x5_clk_periph_setup(struct device_node *np,
+						  struct at91_pmc *pmc);
+
 #endif /* __PMC_H_ */
-- 
1.7.9.5


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

* [PATCH v5 10/17] clk: at91: add peripheral clk macros for peripheral clk dt bindings
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (8 preceding siblings ...)
  2013-11-12 22:08 ` [PATCH v5 09/17] clk: at91: add PMC peripheral clocks Boris BREZILLON
@ 2013-11-13  6:10 ` Boris BREZILLON
  2013-11-13  6:14 ` [PATCH v5 11/17] clk: at91: add PMC programmable clocks Boris BREZILLON
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-13  6:10 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds the peripheral divisors macros (for sam9x5 compatible IPs)
which will be used by peripheral clk dt definitions.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 include/dt-bindings/clk/at91.h |    6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/include/dt-bindings/clk/at91.h b/include/dt-bindings/clk/at91.h
index 0b4cb99..a3b07ca 100644
--- a/include/dt-bindings/clk/at91.h
+++ b/include/dt-bindings/clk/at91.h
@@ -19,4 +19,10 @@
 #define AT91_PMC_MOSCRCS	17		/* Main On-Chip RC */
 #define AT91_PMC_CFDEV		18		/* Clock Failure Detector Event */
 
+/* sam9x5 peripheral divisors */
+#define AT91SAM9X5_PERIPH_CLK_DIV1	0
+#define AT91SAM9X5_PERIPH_CLK_DIV2	1
+#define AT91SAM9X5_PERIPH_CLK_DIV4	2
+#define AT91SAM9X5_PERIPH_CLK_DIV8	3
+
 #endif
-- 
1.7.9.5


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

* [PATCH v5 11/17] clk: at91: add PMC programmable clocks
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (9 preceding siblings ...)
  2013-11-13  6:10 ` [PATCH v5 10/17] clk: at91: add peripheral clk macros for peripheral clk dt bindings Boris BREZILLON
@ 2013-11-13  6:14 ` Boris BREZILLON
  2013-11-13  6:17 ` [PATCH v5 12/17] clk: at91: add PMC utmi clock Boris BREZILLON
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-13  6:14 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds new at91 programmable clocks implementation using common clk
framework.
A programmable clock is a clock which can be exported on a given pin to clock
external devices.
Each programmable clock is given an id (from 0 to 8).
The number of available programmable clocks depends on the SoC you're using.
Programmable clock driver only implements the clock setting (clock rate and
parent setting). It must be chained to a system clock in order to
enable/disable the generated clock.
The PCKX pins used to output the clock signals must be assigned to the
appropriate peripheral (see atmel's datasheets).

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 drivers/clk/at91/Makefile           |    2 +
 drivers/clk/at91/clk-programmable.c |  368 +++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.c              |   15 ++
 drivers/clk/at91/pmc.h              |    9 +
 4 files changed, 394 insertions(+)
 create mode 100644 drivers/clk/at91/clk-programmable.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 04deba3..3873b62 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -5,3 +5,5 @@
 obj-y += pmc.o
 obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o
 obj-y += clk-system.o clk-peripheral.o
+
+obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS)	+= clk-programmable.o
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
new file mode 100644
index 0000000..1daa05f
--- /dev/null
+++ b/drivers/clk/at91/clk-programmable.c
@@ -0,0 +1,368 @@
+/*
+ * drivers/clk/at91/clk-programmable.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include "pmc.h"
+
+#define PROG_SOURCE_MAX		5
+#define PROG_ID_MAX		7
+
+#define PROG_STATUS_MASK(id)	(1 << ((id) + 8))
+#define PROG_PRES_MASK		0x7
+#define PROG_MAX_RM9200_CSS	3
+
+struct clk_programmable_layout {
+	u8 pres_shift;
+	u8 css_mask;
+	u8 have_slck_mck;
+};
+
+struct clk_programmable {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+	unsigned int irq;
+	wait_queue_head_t wait;
+	u8 id;
+	u8 css;
+	u8 pres;
+	u8 slckmck;
+	const struct clk_programmable_layout *layout;
+};
+
+#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
+
+
+static irqreturn_t clk_programmable_irq_handler(int irq, void *dev_id)
+{
+	struct clk_programmable *prog = (struct clk_programmable *)dev_id;
+
+	wake_up(&prog->wait);
+
+	return IRQ_HANDLED;
+}
+
+static int clk_programmable_prepare(struct clk_hw *hw)
+{
+	u32 tmp;
+	struct clk_programmable *prog = to_clk_programmable(hw);
+	struct at91_pmc *pmc = prog->pmc;
+	const struct clk_programmable_layout *layout = prog->layout;
+	u8 id = prog->id;
+	u32 mask = PROG_STATUS_MASK(id);
+
+	tmp = prog->css | (prog->pres << layout->pres_shift);
+	if (layout->have_slck_mck && prog->slckmck)
+		tmp |= AT91_PMC_CSSMCK_MCK;
+
+	pmc_write(pmc, AT91_PMC_PCKR(id), tmp);
+
+	while (!(pmc_read(pmc, AT91_PMC_SR) & mask))
+		wait_event(prog->wait, pmc_read(pmc, AT91_PMC_SR) & mask);
+
+	return 0;
+}
+
+static int clk_programmable_is_ready(struct clk_hw *hw)
+{
+	struct clk_programmable *prog = to_clk_programmable(hw);
+	struct at91_pmc *pmc = prog->pmc;
+
+	return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_PCKR(prog->id));
+}
+
+static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
+						  unsigned long parent_rate)
+{
+	u32 tmp;
+	struct clk_programmable *prog = to_clk_programmable(hw);
+	struct at91_pmc *pmc = prog->pmc;
+	const struct clk_programmable_layout *layout = prog->layout;
+
+	tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
+	prog->pres = (tmp >> layout->pres_shift) & PROG_PRES_MASK;
+
+	return parent_rate >> prog->pres;
+}
+
+static long clk_programmable_round_rate(struct clk_hw *hw, unsigned long rate,
+					unsigned long *parent_rate)
+{
+	unsigned long best_rate = *parent_rate;
+	unsigned long best_diff;
+	unsigned long new_diff;
+	unsigned long cur_rate;
+	int shift = shift;
+
+	if (rate > *parent_rate)
+		return *parent_rate;
+	else
+		best_diff = *parent_rate - rate;
+
+	if (!best_diff)
+		return best_rate;
+
+	for (shift = 1; shift < PROG_PRES_MASK; shift++) {
+		cur_rate = *parent_rate >> shift;
+
+		if (cur_rate > rate)
+			new_diff = cur_rate - rate;
+		else
+			new_diff = rate - cur_rate;
+
+		if (!new_diff)
+			return cur_rate;
+
+		if (new_diff < best_diff) {
+			best_diff = new_diff;
+			best_rate = cur_rate;
+		}
+
+		if (rate > cur_rate)
+			break;
+	}
+
+	return best_rate;
+}
+
+static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct clk_programmable *prog = to_clk_programmable(hw);
+	const struct clk_programmable_layout *layout = prog->layout;
+	if (index > layout->css_mask) {
+		if (index > PROG_MAX_RM9200_CSS && layout->have_slck_mck) {
+			prog->css = 0;
+			prog->slckmck = 1;
+			return 0;
+		} else {
+			return -EINVAL;
+		}
+	}
+
+	prog->css = index;
+	return 0;
+}
+
+static u8 clk_programmable_get_parent(struct clk_hw *hw)
+{
+	u32 tmp;
+	u8 ret;
+	struct clk_programmable *prog = to_clk_programmable(hw);
+	struct at91_pmc *pmc = prog->pmc;
+	const struct clk_programmable_layout *layout = prog->layout;
+
+	tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id));
+	prog->css = tmp & layout->css_mask;
+	ret = prog->css;
+	if (layout->have_slck_mck) {
+		prog->slckmck = !!(tmp & AT91_PMC_CSSMCK_MCK);
+		if (prog->slckmck && !ret)
+			ret = PROG_MAX_RM9200_CSS + 1;
+	}
+
+	return ret;
+}
+
+static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long parent_rate)
+{
+	struct clk_programmable *prog = to_clk_programmable(hw);
+	unsigned long best_rate = parent_rate;
+	unsigned long best_diff;
+	unsigned long new_diff;
+	unsigned long cur_rate;
+	int shift = 0;
+
+	if (rate > parent_rate)
+		return parent_rate;
+	else
+		best_diff = parent_rate - rate;
+
+	if (!best_diff) {
+		prog->pres = shift;
+		return 0;
+	}
+
+	for (shift = 1; shift < PROG_PRES_MASK; shift++) {
+		cur_rate = parent_rate >> shift;
+
+		if (cur_rate > rate)
+			new_diff = cur_rate - rate;
+		else
+			new_diff = rate - cur_rate;
+
+		if (!new_diff)
+			break;
+
+		if (new_diff < best_diff) {
+			best_diff = new_diff;
+			best_rate = cur_rate;
+		}
+
+		if (rate > cur_rate)
+			break;
+	}
+
+	prog->pres = shift;
+	return 0;
+}
+
+static const struct clk_ops programmable_ops = {
+	.prepare = clk_programmable_prepare,
+	.is_prepared = clk_programmable_is_ready,
+	.recalc_rate = clk_programmable_recalc_rate,
+	.round_rate = clk_programmable_round_rate,
+	.get_parent = clk_programmable_get_parent,
+	.set_parent = clk_programmable_set_parent,
+	.set_rate = clk_programmable_set_rate,
+};
+
+static struct clk * __init
+at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq,
+			       const char *name, const char **parent_names,
+			       u8 num_parents, u8 id,
+			       const struct clk_programmable_layout *layout)
+{
+	int ret;
+	struct clk_programmable *prog;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+	char irq_name[11];
+
+	if (id > PROG_ID_MAX)
+		return ERR_PTR(-EINVAL);
+
+	prog = kzalloc(sizeof(*prog), GFP_KERNEL);
+	if (!prog)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &programmable_ops;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+	prog->id = id;
+	prog->layout = layout;
+	prog->hw.init = &init;
+	prog->pmc = pmc;
+	prog->irq = irq;
+	init_waitqueue_head(&prog->wait);
+	irq_set_status_flags(prog->irq, IRQ_NOAUTOEN);
+	snprintf(irq_name, sizeof(irq_name), "clk-prog%d", id);
+	ret = request_irq(prog->irq, clk_programmable_irq_handler,
+			  IRQF_TRIGGER_HIGH, irq_name, prog);
+	if (ret)
+		return ERR_PTR(ret);
+
+	clk = clk_register(NULL, &prog->hw);
+	if (IS_ERR(clk))
+		kfree(prog);
+
+	return clk;
+}
+
+static const struct clk_programmable_layout at91rm9200_programmable_layout = {
+	.pres_shift = 2,
+	.css_mask = 0x3,
+	.have_slck_mck = 0,
+};
+
+static const struct clk_programmable_layout at91sam9g45_programmable_layout = {
+	.pres_shift = 2,
+	.css_mask = 0x3,
+	.have_slck_mck = 1,
+};
+
+static const struct clk_programmable_layout at91sam9x5_programmable_layout = {
+	.pres_shift = 4,
+	.css_mask = 0x7,
+	.have_slck_mck = 0,
+};
+
+static void __init
+of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,
+		       const struct clk_programmable_layout *layout)
+{
+	int num;
+	u32 id;
+	int i;
+	unsigned int irq;
+	struct clk *clk;
+	int num_parents;
+	const char *parent_names[PROG_SOURCE_MAX];
+	const char *name;
+	struct device_node *progclknp;
+
+	num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
+	if (num_parents <= 0 || num_parents > PROG_SOURCE_MAX)
+		return;
+
+	for (i = 0; i < num_parents; ++i) {
+		parent_names[i] = of_clk_get_parent_name(np, i);
+		if (!parent_names[i])
+			return;
+	}
+
+	num = of_get_child_count(np);
+	if (!num || num > (PROG_ID_MAX + 1))
+		return;
+
+	for_each_child_of_node(np, progclknp) {
+		if (of_property_read_u32(progclknp, "reg", &id))
+			continue;
+
+		if (of_property_read_string(np, "clock-output-names", &name))
+			name = progclknp->name;
+
+		irq = irq_of_parse_and_map(np, 0);
+		if (!irq)
+			continue;
+
+		clk = at91_clk_register_programmable(pmc, irq, name,
+						     parent_names, num_parents,
+						     id, layout);
+		if (IS_ERR(clk))
+			continue;
+
+		of_clk_add_provider(progclknp, of_clk_src_simple_get, clk);
+	}
+}
+
+
+void __init of_at91rm9200_clk_prog_setup(struct device_node *np,
+					 struct at91_pmc *pmc)
+{
+	of_at91_clk_prog_setup(np, pmc, &at91rm9200_programmable_layout);
+}
+
+void __init of_at91sam9g45_clk_prog_setup(struct device_node *np,
+					  struct at91_pmc *pmc)
+{
+	of_at91_clk_prog_setup(np, pmc, &at91sam9g45_programmable_layout);
+}
+
+void __init of_at91sam9x5_clk_prog_setup(struct device_node *np,
+					 struct at91_pmc *pmc)
+{
+	of_at91_clk_prog_setup(np, pmc, &at91sam9x5_programmable_layout);
+}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 09dfa63..ffe8ac8 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -280,6 +280,21 @@ static const struct of_device_id pmc_clk_ids[] __initdata = {
 		.compatible = "atmel,at91sam9x5-clk-peripheral",
 		.data = of_at91sam9x5_clk_periph_setup,
 	},
+	/* Programmable clocks */
+#if defined(CONFIG_AT91_PROGRAMMABLE_CLOCKS)
+	{
+		.compatible = "atmel,at91rm9200-clk-programmable",
+		.data = of_at91rm9200_clk_prog_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9g45-clk-programmable",
+		.data = of_at91sam9g45_clk_prog_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-clk-programmable",
+		.data = of_at91sam9x5_clk_prog_setup,
+	},
+#endif
 	{ /*sentinel*/ }
 };
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index a09212b..6eeed49 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -85,4 +85,13 @@ extern void __init of_at91rm9200_clk_periph_setup(struct device_node *np,
 extern void __init of_at91sam9x5_clk_periph_setup(struct device_node *np,
 						  struct at91_pmc *pmc);
 
+#if defined(CONFIG_AT91_PROGRAMMABLE_CLOCKS)
+extern void __init of_at91rm9200_clk_prog_setup(struct device_node *np,
+						struct at91_pmc *pmc);
+extern void __init of_at91sam9g45_clk_prog_setup(struct device_node *np,
+						 struct at91_pmc *pmc);
+extern void __init of_at91sam9x5_clk_prog_setup(struct device_node *np,
+						struct at91_pmc *pmc);
+#endif
+
 #endif /* __PMC_H_ */
-- 
1.7.9.5


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

* [PATCH v5 12/17] clk: at91: add PMC utmi clock
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (10 preceding siblings ...)
  2013-11-13  6:14 ` [PATCH v5 11/17] clk: at91: add PMC programmable clocks Boris BREZILLON
@ 2013-11-13  6:17 ` Boris BREZILLON
  2013-11-13  6:20 ` [PATCH v5 13/17] clk: at91: add PMC usb clock Boris BREZILLON
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-13  6:17 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This adds new at91 utmi clock implementation using common clk framework.

This clock is a pll with a fixed factor (x40).
It is used as a source for usb clock.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/Kconfig  |    7 ++
 drivers/clk/at91/Makefile   |    1 +
 drivers/clk/at91/clk-utmi.c |  162 +++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.c      |    7 ++
 drivers/clk/at91/pmc.h      |    5 ++
 5 files changed, 182 insertions(+)
 create mode 100644 drivers/clk/at91/clk-utmi.c

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 85b53a4..6ad37da 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -1,5 +1,8 @@
 if ARCH_AT91
 
+config HAVE_AT91_UTMI
+	bool
+
 config HAVE_AT91_DBGU0
 	bool
 
@@ -78,6 +81,7 @@ config SOC_SAMA5D3
 	select HAVE_FB_ATMEL
 	select HAVE_AT91_DBGU1
 	select AT91_USE_OLD_CLK
+	select HAVE_AT91_UTMI
 	help
 	  Select this if you are using one of Atmel's SAMA5D3 family SoC.
 	  This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35.
@@ -124,6 +128,7 @@ config SOC_AT91SAM9RL
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
+	select HAVE_AT91_UTMI
 
 config SOC_AT91SAM9G45
 	bool "AT91SAM9G45 or AT91SAM9M10 families"
@@ -131,6 +136,7 @@ config SOC_AT91SAM9G45
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
+	select HAVE_AT91_UTMI
 	help
 	  Select this if you are using one of Atmel's AT91SAM9G45 family SoC.
 	  This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11.
@@ -141,6 +147,7 @@ config SOC_AT91SAM9X5
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
+	select HAVE_AT91_UTMI
 	help
 	  Select this if you are using one of Atmel's AT91SAM9x5 family SoC.
 	  This means that your SAM9 name finishes with a '5' (except if it is
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 3873b62..a824883 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -7,3 +7,4 @@ obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o
 obj-y += clk-system.o clk-peripheral.o
 
 obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS)	+= clk-programmable.o
+obj-$(CONFIG_HAVE_AT91_UTMI)		+= clk-utmi.o
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
new file mode 100644
index 0000000..9a133df
--- /dev/null
+++ b/drivers/clk/at91/clk-utmi.c
@@ -0,0 +1,162 @@
+/*
+ * drivers/clk/at91/clk-utmi.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "pmc.h"
+
+#define UTMI_FIXED_MUL		40
+
+struct clk_utmi {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+	unsigned int irq;
+	wait_queue_head_t wait;
+};
+
+#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
+
+static irqreturn_t clk_utmi_irq_handler(int irq, void *dev_id)
+{
+	struct clk_utmi *utmi = (struct clk_utmi *)dev_id;
+
+	wake_up(&utmi->wait);
+	disable_irq_nosync(utmi->irq);
+
+	return IRQ_HANDLED;
+}
+
+static int clk_utmi_prepare(struct clk_hw *hw)
+{
+	struct clk_utmi *utmi = to_clk_utmi(hw);
+	struct at91_pmc *pmc = utmi->pmc;
+	u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) | AT91_PMC_UPLLEN |
+		  AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN;
+
+	pmc_write(pmc, AT91_CKGR_UCKR, tmp);
+
+	while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU)) {
+		enable_irq(utmi->irq);
+		wait_event(utmi->wait,
+			   pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU);
+	}
+
+	return 0;
+}
+
+static int clk_utmi_is_ready(struct clk_hw *hw)
+{
+	struct clk_utmi *utmi = to_clk_utmi(hw);
+	struct at91_pmc *pmc = utmi->pmc;
+
+	return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_LOCKU);
+}
+
+static void clk_utmi_disable(struct clk_hw *hw)
+{
+	struct clk_utmi *utmi = to_clk_utmi(hw);
+	struct at91_pmc *pmc = utmi->pmc;
+	u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) & ~AT91_PMC_UPLLEN;
+
+	pmc_write(pmc, AT91_CKGR_UCKR, tmp);
+}
+
+static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	/* UTMI clk is a fixed clk multiplier */
+	return parent_rate * UTMI_FIXED_MUL;
+}
+
+static const struct clk_ops utmi_ops = {
+	.prepare = clk_utmi_prepare,
+	.is_prepared = clk_utmi_is_ready,
+	.disable = clk_utmi_disable,
+	.is_enabled = clk_utmi_is_ready,
+	.recalc_rate = clk_utmi_recalc_rate,
+};
+
+static struct clk * __init
+at91_clk_register_utmi(struct at91_pmc *pmc, unsigned int irq,
+		       const char *name, const char *parent_name)
+{
+	int ret;
+	struct clk_utmi *utmi;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
+	if (!utmi)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &utmi_ops;
+	init.parent_names = parent_name ? &parent_name : NULL;
+	init.num_parents = parent_name ? 1 : 0;
+	init.flags = CLK_SET_RATE_GATE;
+
+	utmi->hw.init = &init;
+	utmi->pmc = pmc;
+	utmi->irq = irq;
+	init_waitqueue_head(&utmi->wait);
+	irq_set_status_flags(utmi->irq, IRQ_NOAUTOEN);
+	ret = request_irq(utmi->irq, clk_utmi_irq_handler,
+			  IRQF_TRIGGER_HIGH, "clk-utmi", utmi);
+	if (ret)
+		return ERR_PTR(ret);
+
+	clk = clk_register(NULL, &utmi->hw);
+	if (IS_ERR(clk))
+		kfree(utmi);
+
+	return clk;
+}
+
+static void __init
+of_at91_clk_utmi_setup(struct device_node *np, struct at91_pmc *pmc)
+{
+	unsigned int irq;
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	irq = irq_of_parse_and_map(np, 0);
+	if (!irq)
+		return;
+
+	clk = at91_clk_register_utmi(pmc, irq, name, parent_name);
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+	return;
+}
+
+void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np,
+					 struct at91_pmc *pmc)
+{
+	of_at91_clk_utmi_setup(np, pmc);
+}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index ffe8ac8..009d1b2 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -295,6 +295,13 @@ static const struct of_device_id pmc_clk_ids[] __initdata = {
 		.data = of_at91sam9x5_clk_prog_setup,
 	},
 #endif
+	/* UTMI clock */
+#if defined(CONFIG_HAVE_AT91_UTMI)
+	{
+		.compatible = "atmel,at91sam9x5-clk-utmi",
+		.data = of_at91sam9x5_clk_utmi_setup,
+	},
+#endif
 	{ /*sentinel*/ }
 };
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 6eeed49..8e18ab0 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -94,4 +94,9 @@ extern void __init of_at91sam9x5_clk_prog_setup(struct device_node *np,
 						struct at91_pmc *pmc);
 #endif
 
+#if defined(CONFIG_HAVE_AT91_UTMI)
+extern void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np,
+						struct at91_pmc *pmc);
+#endif
+
 #endif /* __PMC_H_ */
-- 
1.7.9.5


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

* [PATCH v5 13/17] clk: at91: add PMC usb clock
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (11 preceding siblings ...)
  2013-11-13  6:17 ` [PATCH v5 12/17] clk: at91: add PMC utmi clock Boris BREZILLON
@ 2013-11-13  6:20 ` Boris BREZILLON
  2013-11-13  6:22 ` [PATCH v5 14/17] clk: at91: add PMC smd clock Boris BREZILLON
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-13  6:20 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds new at91 usb clock implementation using common clk framework.
This clock is used to clock usb ports (ohci, ehci and udc).

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/Kconfig |   11 ++
 drivers/clk/at91/Makefile  |    1 +
 drivers/clk/at91/clk-usb.c |  400 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.c     |   15 ++
 drivers/clk/at91/pmc.h     |    9 +
 5 files changed, 436 insertions(+)
 create mode 100644 drivers/clk/at91/clk-usb.c

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 6ad37da..b76dc4c 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -3,6 +3,9 @@ if ARCH_AT91
 config HAVE_AT91_UTMI
 	bool
 
+config HAVE_AT91_USB_CLK
+	bool
+
 config HAVE_AT91_DBGU0
 	bool
 
@@ -82,6 +85,7 @@ config SOC_SAMA5D3
 	select HAVE_AT91_DBGU1
 	select AT91_USE_OLD_CLK
 	select HAVE_AT91_UTMI
+	select HAVE_AT91_USB_CLK
 	help
 	  Select this if you are using one of Atmel's SAMA5D3 family SoC.
 	  This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35.
@@ -96,12 +100,14 @@ config SOC_AT91RM9200
 	select MULTI_IRQ_HANDLER
 	select SPARSE_IRQ
 	select AT91_USE_OLD_CLK
+	select HAVE_AT91_USB_CLK
 
 config SOC_AT91SAM9260
 	bool "AT91SAM9260, AT91SAM9XE or AT91SAM9G20"
 	select HAVE_AT91_DBGU0
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
+	select HAVE_AT91_USB_CLK
 	help
 	  Select this if you are using one of Atmel's AT91SAM9260, AT91SAM9XE
 	  or AT91SAM9G20 SoC.
@@ -112,6 +118,7 @@ config SOC_AT91SAM9261
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
+	select HAVE_AT91_USB_CLK
 	help
 	  Select this if you are using one of Atmel's AT91SAM9261 or AT91SAM9G10 SoC.
 
@@ -121,6 +128,7 @@ config SOC_AT91SAM9263
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
+	select HAVE_AT91_USB_CLK
 
 config SOC_AT91SAM9RL
 	bool "AT91SAM9RL"
@@ -137,6 +145,7 @@ config SOC_AT91SAM9G45
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
 	select HAVE_AT91_UTMI
+	select HAVE_AT91_USB_CLK
 	help
 	  Select this if you are using one of Atmel's AT91SAM9G45 family SoC.
 	  This support covers AT91SAM9G45, AT91SAM9G46, AT91SAM9M10 and AT91SAM9M11.
@@ -148,6 +157,7 @@ config SOC_AT91SAM9X5
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
 	select HAVE_AT91_UTMI
+	select HAVE_AT91_USB_CLK
 	help
 	  Select this if you are using one of Atmel's AT91SAM9x5 family SoC.
 	  This means that your SAM9 name finishes with a '5' (except if it is
@@ -161,6 +171,7 @@ config SOC_AT91SAM9N12
 	select HAVE_FB_ATMEL
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
+	select HAVE_AT91_USB_CLK
 	help
 	  Select this if you are using Atmel's AT91SAM9N12 SoC.
 
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index a824883..61db058 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -8,3 +8,4 @@ obj-y += clk-system.o clk-peripheral.o
 
 obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS)	+= clk-programmable.o
 obj-$(CONFIG_HAVE_AT91_UTMI)		+= clk-utmi.o
+obj-$(CONFIG_HAVE_AT91_USB_CLK)		+= clk-usb.o
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
new file mode 100644
index 0000000..0454555
--- /dev/null
+++ b/drivers/clk/at91/clk-usb.c
@@ -0,0 +1,400 @@
+/*
+ * drivers/clk/at91/clk-usb.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+
+#include "pmc.h"
+
+#define USB_SOURCE_MAX		2
+
+#define SAM9X5_USB_DIV_SHIFT	8
+#define SAM9X5_USB_MAX_DIV	0xf
+
+#define RM9200_USB_DIV_SHIFT	28
+#define RM9200_USB_DIV_TAB_SIZE	4
+
+struct at91sam9x5_clk_usb {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+};
+
+#define to_at91sam9x5_clk_usb(hw) \
+	container_of(hw, struct at91sam9x5_clk_usb, hw)
+
+struct at91rm9200_clk_usb {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+	u32 divisors[4];
+};
+
+#define to_at91rm9200_clk_usb(hw) \
+	container_of(hw, struct at91rm9200_clk_usb, hw)
+
+static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
+						    unsigned long parent_rate)
+{
+	u32 tmp;
+	u8 usbdiv;
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
+	struct at91_pmc *pmc = usb->pmc;
+
+	tmp = pmc_read(pmc, AT91_PMC_USB);
+	usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
+	return parent_rate / (usbdiv + 1);
+}
+
+static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
+					  unsigned long *parent_rate)
+{
+	unsigned long div;
+	unsigned long bestrate;
+	unsigned long tmp;
+
+	if (rate >= *parent_rate)
+		return *parent_rate;
+
+	div = *parent_rate / rate;
+	if (div >= SAM9X5_USB_MAX_DIV)
+		return *parent_rate / (SAM9X5_USB_MAX_DIV + 1);
+
+	bestrate = *parent_rate / div;
+	tmp = *parent_rate / (div + 1);
+	if (bestrate - rate > rate - tmp)
+		bestrate = tmp;
+
+	return bestrate;
+}
+
+static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
+{
+	u32 tmp;
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
+	struct at91_pmc *pmc = usb->pmc;
+
+	if (index > 1)
+		return -EINVAL;
+	tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_USBS;
+	if (index)
+		tmp |= AT91_PMC_USBS;
+	pmc_write(pmc, AT91_PMC_USB, tmp);
+	return 0;
+}
+
+static u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
+	struct at91_pmc *pmc = usb->pmc;
+
+	return pmc_read(pmc, AT91_PMC_USB) & AT91_PMC_USBS;
+}
+
+static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
+				       unsigned long parent_rate)
+{
+	u32 tmp;
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
+	struct at91_pmc *pmc = usb->pmc;
+	unsigned long div = parent_rate / rate;
+
+	if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV)
+		return -EINVAL;
+
+	tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV;
+	tmp |= (div - 1) << SAM9X5_USB_DIV_SHIFT;
+	pmc_write(pmc, AT91_PMC_USB, tmp);
+
+	return 0;
+}
+
+static const struct clk_ops at91sam9x5_usb_ops = {
+	.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
+	.round_rate = at91sam9x5_clk_usb_round_rate,
+	.get_parent = at91sam9x5_clk_usb_get_parent,
+	.set_parent = at91sam9x5_clk_usb_set_parent,
+	.set_rate = at91sam9x5_clk_usb_set_rate,
+};
+
+static int at91sam9n12_clk_usb_enable(struct clk_hw *hw)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
+	struct at91_pmc *pmc = usb->pmc;
+
+	pmc_write(pmc, AT91_PMC_USB,
+		  pmc_read(pmc, AT91_PMC_USB) | AT91_PMC_USBS);
+	return 0;
+}
+
+static void at91sam9n12_clk_usb_disable(struct clk_hw *hw)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
+	struct at91_pmc *pmc = usb->pmc;
+
+	pmc_write(pmc, AT91_PMC_USB,
+		  pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_USBS);
+}
+
+static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
+	struct at91_pmc *pmc = usb->pmc;
+
+	return !!(pmc_read(pmc, AT91_PMC_USB) & AT91_PMC_USBS);
+}
+
+static const struct clk_ops at91sam9n12_usb_ops = {
+	.enable = at91sam9n12_clk_usb_enable,
+	.disable = at91sam9n12_clk_usb_disable,
+	.is_enabled = at91sam9n12_clk_usb_is_enabled,
+	.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
+	.round_rate = at91sam9x5_clk_usb_round_rate,
+	.set_rate = at91sam9x5_clk_usb_set_rate,
+};
+
+static struct clk * __init
+at91sam9x5_clk_register_usb(struct at91_pmc *pmc, const char *name,
+			    const char **parent_names, u8 num_parents)
+{
+	struct at91sam9x5_clk_usb *usb;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+	if (!usb)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &at91sam9x5_usb_ops;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+	usb->hw.init = &init;
+	usb->pmc = pmc;
+
+	clk = clk_register(NULL, &usb->hw);
+	if (IS_ERR(clk))
+		kfree(usb);
+
+	return clk;
+}
+
+static struct clk * __init
+at91sam9n12_clk_register_usb(struct at91_pmc *pmc, const char *name,
+			     const char *parent_name)
+{
+	struct at91sam9x5_clk_usb *usb;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+	if (!usb)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &at91sam9n12_usb_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE;
+
+	usb->hw.init = &init;
+	usb->pmc = pmc;
+
+	clk = clk_register(NULL, &usb->hw);
+	if (IS_ERR(clk))
+		kfree(usb);
+
+	return clk;
+}
+
+static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
+						    unsigned long parent_rate)
+{
+	struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
+	struct at91_pmc *pmc = usb->pmc;
+	u32 tmp;
+	u8 usbdiv;
+
+	tmp = pmc_read(pmc, AT91_CKGR_PLLBR);
+	usbdiv = (tmp & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT;
+	if (usb->divisors[usbdiv])
+		return parent_rate / usb->divisors[usbdiv];
+
+	return 0;
+}
+
+static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
+					  unsigned long *parent_rate)
+{
+	struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
+	unsigned long bestrate = 0;
+	int bestdiff = -1;
+	unsigned long tmprate;
+	int tmpdiff;
+	int i = 0;
+
+	for (i = 0; i < 4; i++) {
+		if (!usb->divisors[i])
+			continue;
+		tmprate = *parent_rate / usb->divisors[i];
+		if (tmprate < rate)
+			tmpdiff = rate - tmprate;
+		else
+			tmpdiff = tmprate - rate;
+
+		if (bestdiff < 0 || bestdiff > tmpdiff) {
+			bestrate = tmprate;
+			bestdiff = tmpdiff;
+		}
+
+		if (!bestdiff)
+			break;
+	}
+
+	return bestrate;
+}
+
+static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
+				       unsigned long parent_rate)
+{
+	u32 tmp;
+	int i;
+	struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
+	struct at91_pmc *pmc = usb->pmc;
+	unsigned long div = parent_rate / rate;
+
+	if (parent_rate % rate)
+		return -EINVAL;
+	for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
+		if (usb->divisors[i] == div) {
+			tmp = pmc_read(pmc, AT91_CKGR_PLLBR) &
+			      ~AT91_PMC_USBDIV;
+			tmp |= i << RM9200_USB_DIV_SHIFT;
+			pmc_write(pmc, AT91_CKGR_PLLBR, tmp);
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static const struct clk_ops at91rm9200_usb_ops = {
+	.recalc_rate = at91rm9200_clk_usb_recalc_rate,
+	.round_rate = at91rm9200_clk_usb_round_rate,
+	.set_rate = at91rm9200_clk_usb_set_rate,
+};
+
+static struct clk * __init
+at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name,
+			    const char *parent_name, const u32 *divisors)
+{
+	struct at91rm9200_clk_usb *usb;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+	if (!usb)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &at91rm9200_usb_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	init.flags = 0;
+
+	usb->hw.init = &init;
+	usb->pmc = pmc;
+	memcpy(usb->divisors, divisors, sizeof(usb->divisors));
+
+	clk = clk_register(NULL, &usb->hw);
+	if (IS_ERR(clk))
+		kfree(usb);
+
+	return clk;
+}
+
+void __init of_at91sam9x5_clk_usb_setup(struct device_node *np,
+					struct at91_pmc *pmc)
+{
+	struct clk *clk;
+	int i;
+	int num_parents;
+	const char *parent_names[USB_SOURCE_MAX];
+	const char *name = np->name;
+
+	num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
+	if (num_parents <= 0 || num_parents > USB_SOURCE_MAX)
+		return;
+
+	for (i = 0; i < num_parents; i++) {
+		parent_names[i] = of_clk_get_parent_name(np, i);
+		if (!parent_names[i])
+			return;
+	}
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	clk = at91sam9x5_clk_register_usb(pmc, name, parent_names, num_parents);
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+void __init of_at91sam9n12_clk_usb_setup(struct device_node *np,
+					 struct at91_pmc *pmc)
+{
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	if (!parent_name)
+		return;
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	clk = at91sam9n12_clk_register_usb(pmc, name, parent_name);
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+void __init of_at91rm9200_clk_usb_setup(struct device_node *np,
+					struct at91_pmc *pmc)
+{
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+	u32 divisors[4] = {0, 0, 0, 0};
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	if (!parent_name)
+		return;
+
+	of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4);
+	if (!divisors[0])
+		return;
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	clk = at91rm9200_clk_register_usb(pmc, name, parent_name, divisors);
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 009d1b2..2a8b08e 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -302,6 +302,21 @@ static const struct of_device_id pmc_clk_ids[] __initdata = {
 		.data = of_at91sam9x5_clk_utmi_setup,
 	},
 #endif
+	/* USB clock */
+#if defined(CONFIG_HAVE_AT91_USB_CLK)
+	{
+		.compatible = "atmel,at91rm9200-clk-usb",
+		.data = of_at91rm9200_clk_usb_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-clk-usb",
+		.data = of_at91sam9x5_clk_usb_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9n12-clk-usb",
+		.data = of_at91sam9n12_clk_usb_setup,
+	},
+#endif
 	{ /*sentinel*/ }
 };
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 8e18ab0..2483ddc 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -99,4 +99,13 @@ extern void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np,
 						struct at91_pmc *pmc);
 #endif
 
+#if defined(CONFIG_HAVE_AT91_USB_CLK)
+extern void __init of_at91rm9200_clk_usb_setup(struct device_node *np,
+					       struct at91_pmc *pmc);
+extern void __init of_at91sam9x5_clk_usb_setup(struct device_node *np,
+					       struct at91_pmc *pmc);
+extern void __init of_at91sam9n12_clk_usb_setup(struct device_node *np,
+						struct at91_pmc *pmc);
+#endif
+
 #endif /* __PMC_H_ */
-- 
1.7.9.5


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

* [PATCH v5 14/17] clk: at91: add PMC smd clock
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (12 preceding siblings ...)
  2013-11-13  6:20 ` [PATCH v5 13/17] clk: at91: add PMC usb clock Boris BREZILLON
@ 2013-11-13  6:22 ` Boris BREZILLON
  2013-11-13  7:12 ` [PATCH v5 15/17] dt: binding: add at91 clks dt bindings documentation Boris BREZILLON
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-13  6:22 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds at91 smd (Soft Modem) clock implementation using common clk
framework.

Not used by any driver right now.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/Kconfig |    5 ++
 drivers/clk/at91/Makefile  |    1 +
 drivers/clk/at91/clk-smd.c |  173 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.c     |    7 ++
 drivers/clk/at91/pmc.h     |    5 ++
 5 files changed, 191 insertions(+)
 create mode 100644 drivers/clk/at91/clk-smd.c

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index b76dc4c..97033f7 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -39,6 +39,9 @@ config AT91_SAM9G45_RESET
 config AT91_SAM9_TIME
 	bool
 
+config HAVE_AT91_SMD
+	bool
+
 config SOC_AT91SAM9
 	bool
 	select AT91_SAM9_TIME
@@ -85,6 +88,7 @@ config SOC_SAMA5D3
 	select HAVE_AT91_DBGU1
 	select AT91_USE_OLD_CLK
 	select HAVE_AT91_UTMI
+	select HAVE_AT91_SMD
 	select HAVE_AT91_USB_CLK
 	help
 	  Select this if you are using one of Atmel's SAMA5D3 family SoC.
@@ -157,6 +161,7 @@ config SOC_AT91SAM9X5
 	select SOC_AT91SAM9
 	select AT91_USE_OLD_CLK
 	select HAVE_AT91_UTMI
+	select HAVE_AT91_SMD
 	select HAVE_AT91_USB_CLK
 	help
 	  Select this if you are using one of Atmel's AT91SAM9x5 family SoC.
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 61db058..0e92b71 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -9,3 +9,4 @@ obj-y += clk-system.o clk-peripheral.o
 obj-$(CONFIG_AT91_PROGRAMMABLE_CLOCKS)	+= clk-programmable.o
 obj-$(CONFIG_HAVE_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_HAVE_AT91_USB_CLK)		+= clk-usb.o
+obj-$(CONFIG_HAVE_AT91_SMD)		+= clk-smd.o
diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c
new file mode 100644
index 0000000..9f3fa39
--- /dev/null
+++ b/drivers/clk/at91/clk-smd.c
@@ -0,0 +1,173 @@
+/*
+ * drivers/clk/at91/clk-smd.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+
+#include "pmc.h"
+
+#define SMD_SOURCE_MAX		2
+
+#define SMD_DIV_SHIFT		8
+#define SMD_MAX_DIV		0xf
+
+struct at91sam9x5_clk_smd {
+	struct clk_hw hw;
+	struct at91_pmc *pmc;
+};
+
+#define to_at91sam9x5_clk_smd(hw) \
+	container_of(hw, struct at91sam9x5_clk_smd, hw)
+
+static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw *hw,
+						    unsigned long parent_rate)
+{
+	u32 tmp;
+	u8 smddiv;
+	struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
+	struct at91_pmc *pmc = smd->pmc;
+
+	tmp = pmc_read(pmc, AT91_PMC_SMD);
+	smddiv = (tmp & AT91_PMC_SMD_DIV) >> SMD_DIV_SHIFT;
+	return parent_rate / (smddiv + 1);
+}
+
+static long at91sam9x5_clk_smd_round_rate(struct clk_hw *hw, unsigned long rate,
+					  unsigned long *parent_rate)
+{
+	unsigned long div;
+	unsigned long bestrate;
+	unsigned long tmp;
+
+	if (rate >= *parent_rate)
+		return *parent_rate;
+
+	div = *parent_rate / rate;
+	if (div > SMD_MAX_DIV)
+		return *parent_rate / (SMD_MAX_DIV + 1);
+
+	bestrate = *parent_rate / div;
+	tmp = *parent_rate / (div + 1);
+	if (bestrate - rate > rate - tmp)
+		bestrate = tmp;
+
+	return bestrate;
+}
+
+static int at91sam9x5_clk_smd_set_parent(struct clk_hw *hw, u8 index)
+{
+	u32 tmp;
+	struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
+	struct at91_pmc *pmc = smd->pmc;
+
+	if (index > 1)
+		return -EINVAL;
+	tmp = pmc_read(pmc, AT91_PMC_SMD) & ~AT91_PMC_SMDS;
+	if (index)
+		tmp |= AT91_PMC_SMDS;
+	pmc_write(pmc, AT91_PMC_SMD, tmp);
+	return 0;
+}
+
+static u8 at91sam9x5_clk_smd_get_parent(struct clk_hw *hw)
+{
+	struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
+	struct at91_pmc *pmc = smd->pmc;
+
+	return pmc_read(pmc, AT91_PMC_SMD) & AT91_PMC_SMDS;
+}
+
+static int at91sam9x5_clk_smd_set_rate(struct clk_hw *hw, unsigned long rate,
+				       unsigned long parent_rate)
+{
+	u32 tmp;
+	struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
+	struct at91_pmc *pmc = smd->pmc;
+	unsigned long div = parent_rate / rate;
+
+	if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1))
+		return -EINVAL;
+	tmp = pmc_read(pmc, AT91_PMC_SMD) & ~AT91_PMC_SMD_DIV;
+	tmp |= (div - 1) << SMD_DIV_SHIFT;
+	pmc_write(pmc, AT91_PMC_SMD, tmp);
+
+	return 0;
+}
+
+static const struct clk_ops at91sam9x5_smd_ops = {
+	.recalc_rate = at91sam9x5_clk_smd_recalc_rate,
+	.round_rate = at91sam9x5_clk_smd_round_rate,
+	.get_parent = at91sam9x5_clk_smd_get_parent,
+	.set_parent = at91sam9x5_clk_smd_set_parent,
+	.set_rate = at91sam9x5_clk_smd_set_rate,
+};
+
+static struct clk * __init
+at91sam9x5_clk_register_smd(struct at91_pmc *pmc, const char *name,
+			    const char **parent_names, u8 num_parents)
+{
+	struct at91sam9x5_clk_smd *smd;
+	struct clk *clk = NULL;
+	struct clk_init_data init;
+
+	smd = kzalloc(sizeof(*smd), GFP_KERNEL);
+	if (!smd)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &at91sam9x5_smd_ops;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+	smd->hw.init = &init;
+	smd->pmc = pmc;
+
+	clk = clk_register(NULL, &smd->hw);
+	if (IS_ERR(clk))
+		kfree(smd);
+
+	return clk;
+}
+
+void __init of_at91sam9x5_clk_smd_setup(struct device_node *np,
+					struct at91_pmc *pmc)
+{
+	struct clk *clk;
+	int i;
+	int num_parents;
+	const char *parent_names[SMD_SOURCE_MAX];
+	const char *name = np->name;
+
+	num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
+	if (num_parents <= 0 || num_parents >= SMD_SOURCE_MAX)
+		return;
+
+	for (i = 0; i < num_parents; i++) {
+		parent_names[i] = of_clk_get_parent_name(np, i);
+		if (!parent_names[i])
+			return;
+	}
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	clk = at91sam9x5_clk_register_smd(pmc, name, parent_names,
+					  num_parents);
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 2a8b08e..7da5d91 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -317,6 +317,13 @@ static const struct of_device_id pmc_clk_ids[] __initdata = {
 		.data = of_at91sam9n12_clk_usb_setup,
 	},
 #endif
+	/* SMD clock */
+#if defined(CONFIG_HAVE_AT91_SMD)
+	{
+		.compatible = "atmel,at91sam9x5-clk-smd",
+		.data = of_at91sam9x5_clk_smd_setup,
+	},
+#endif
 	{ /*sentinel*/ }
 };
 
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 2483ddc..ba8d142 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -108,4 +108,9 @@ extern void __init of_at91sam9n12_clk_usb_setup(struct device_node *np,
 						struct at91_pmc *pmc);
 #endif
 
+#if defined(CONFIG_HAVE_AT91_SMD)
+extern void __init of_at91sam9x5_clk_smd_setup(struct device_node *np,
+					       struct at91_pmc *pmc);
+#endif
+
 #endif /* __PMC_H_ */
-- 
1.7.9.5


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

* [PATCH v5 15/17] dt: binding: add at91 clks dt bindings documentation
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (13 preceding siblings ...)
  2013-11-13  6:22 ` [PATCH v5 14/17] clk: at91: add PMC smd clock Boris BREZILLON
@ 2013-11-13  7:12 ` Boris BREZILLON
  2013-11-13  7:15 ` [PATCH v5 16/17] ARM: at91: move pit timer to common clk framework Boris BREZILLON
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-13  7:12 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds new at91 clks dt bindings documentation.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 .../devicetree/bindings/clock/at91-clock.txt       |  339 ++++++++++++++++++++
 1 file changed, 339 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/at91-clock.txt

diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
new file mode 100644
index 0000000..cd5e239
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
@@ -0,0 +1,339 @@
+Device Tree Clock bindings for arch-at91
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be one of the following:
+	"atmel,at91rm9200-pmc" or
+	"atmel,at91sam9g45-pmc" or
+	"atmel,at91sam9n12-pmc" or
+	"atmel,at91sam9x5-pmc" or
+	"atmel,sama5d3-pmc":
+		at91 PMC (Power Management Controller)
+		All at91 specific clocks (clocks defined below) must be child
+		node of the PMC node.
+
+	"atmel,at91rm9200-clk-main":
+		at91 main oscillator
+
+	"atmel,at91rm9200-clk-master" or
+	"atmel,at91sam9x5-clk-master":
+		at91 master clock
+
+	"atmel,at91sam9x5-clk-peripheral" or
+	"atmel,at91rm9200-clk-peripheral":
+		at91 peripheral clocks
+
+	"atmel,at91rm9200-clk-pll" or
+	"atmel,at91sam9g45-clk-pll" or
+	"atmel,at91sam9g20-clk-pllb" or
+	"atmel,sama5d3-clk-pll":
+		at91 pll clocks
+
+	"atmel,at91sam9x5-clk-plldiv":
+		at91 plla divisor
+
+	"atmel,at91rm9200-clk-programmable" or
+	"atmel,at91sam9g45-clk-programmable" or
+	"atmel,at91sam9x5-clk-programmable":
+		at91 programmable clocks
+
+	"atmel,at91sam9x5-clk-smd":
+		at91 SMD (Soft Modem) clock
+
+	"atmel,at91rm9200-clk-system":
+		at91 system clocks
+
+	"atmel,at91rm9200-clk-usb" or
+	"atmel,at91sam9x5-clk-usb" or
+	"atmel,at91sam9n12-clk-usb":
+		at91 usb clock
+
+	"atmel,at91sam9x5-clk-utmi":
+		at91 utmi clock
+
+Required properties for PMC node:
+- reg : defines the IO memory reserved for the PMC.
+- #size-cells : shall be 0 (reg is used to encode clk id).
+- #address-cells : shall be 1 (reg is used to encode clk id).
+- interrupts : shall be set to PMC interrupt line.
+- interrupt-controller : tell that the PMC is an interrupt controller.
+- #interrupt-cells : must be set to 1. The first cell encodes the interrupt id,
+	and reflect the bit position in the PMC_ER/DR/SR registers.
+	You can use the dt macros defined in dt-bindings/clk/at91.h.
+	0 (AT91_PMC_MOSCS) -> main oscillator ready
+	1 (AT91_PMC_LOCKA) -> PLL A ready
+	2 (AT91_PMC_LOCKB) -> PLL B ready
+	3 (AT91_PMC_MCKRDY) -> master clock ready
+	6 (AT91_PMC_LOCKU) -> UTMI PLL clock ready
+	8 .. 15 (AT91_PMC_PCKRDY(id)) -> programmable clock ready
+	16 (AT91_PMC_MOSCSELS) -> main oscillator selected
+	17 (AT91_PMC_MOSCRCS) -> RC main oscillator stabilized
+	18 (AT91_PMC_CFDEV) -> clock failure detected
+
+For example:
+	pmc: pmc@fffffc00 {
+		compatible = "atmel,sama5d3-pmc";
+		interrupts = <1 4 7>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		#size-cells = <0>;
+		#address-cells = <1>;
+
+		/* put at91 clocks here */
+	};
+
+Required properties for main clock:
+- interrupt-parent : must reference the PMC node.
+- interrupts : shall be set to "<0>".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks (optional if clock-frequency is provided) : shall be the slow clock
+	phandle. This clock is used to calculate the main clock rate if
+	"clock-frequency" is not provided.
+- clock-frequency : the main oscillator frequency.Prefer the use of
+	"clock-frequency" over automatic clock rate calculation.
+
+For example:
+	main: mainck {
+		compatible = "atmel,at91rm9200-clk-main";
+		interrupt-parent = <&pmc>;
+		interrupts = <0>;
+		#clock-cells = <0>;
+		clocks = <&ck32k>;
+		clock-frequency = <18432000>;
+	};
+
+Required properties for master clock:
+- interrupt-parent : must reference the PMC node.
+- interrupts : shall be set to "<3>".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : shall be the master clock sources (see atmel datasheet) phandles.
+	e.g. "<&ck32k>, <&main>, <&plla>, <&pllb>".
+- atmel,clk-output-range : minimum and maximum clock frequency (two u32
+			   fields).
+	   e.g. output = <0 133000000>; <=> 0 to 133MHz.
+- atmel,clk-divisors : master clock divisors table (four u32 fields).
+		0 <=> reserved value.
+		e.g. divisors = <1 2 4 6>;
+- atmel,master-clk-have-div3-pres : some SoC use the reserved value 7 in the
+				    PRES field as CLOCK_DIV3 (e.g sam9x5).
+
+For example:
+	mck: mck {
+		compatible = "atmel,at91rm9200-clk-master";
+		interrupt-parent = <&pmc>;
+		interrupts = <3>;
+		#clock-cells = <0>;
+		atmel,clk-output-range = <0 133000000>;
+		atmel,clk-divisors = <1 2 4 0>;
+	};
+
+Required properties for peripheral clocks:
+- #size-cells : shall be 0 (reg is used to encode clk id).
+- #address-cells : shall be 1 (reg is used to encode clk id).
+- clocks : shall be the master clock phandle.
+	e.g. clocks = <&mck>;
+- name: device tree node describing a specific system clock.
+	* #clock-cells : from common clock binding; shall be set to 0.
+	* reg: peripheral id. See Atmel's datasheets to get a full
+	  list of peripheral ids.
+	* atmel,clk-output-range : minimum and maximum clock frequency
+	  (two u32 fields). Only valid on at91sam9x5-clk-peripheral
+	  compatible IPs.
+
+For example:
+	periph: periphck {
+		compatible = "atmel,at91sam9x5-clk-peripheral";
+		#size-cells = <0>;
+		#address-cells = <1>;
+		clocks = <&mck>;
+
+		ssc0_clk {
+			#clock-cells = <0>;
+			reg = <2>;
+			atmel,clk-output-range = <0 133000000>;
+		};
+
+		usart0_clk {
+			#clock-cells = <0>;
+			reg = <3>;
+			atmel,clk-output-range = <0 66000000>;
+		};
+	};
+
+
+Required properties for pll clocks:
+- interrupt-parent : must reference the PMC node.
+- interrupts : shall be set to "<1>".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : shall be the main clock phandle.
+- reg : pll id.
+	0 -> PLL A
+	1 -> PLL B
+- atmel,clk-input-range : minimum and maximum source clock frequency (two u32
+			  fields).
+	  e.g. input = <1 32000000>; <=> 1 to 32MHz.
+- #atmel,pll-clk-output-range-cells : number of cells reserved for pll output
+				      range description. Sould be set to 2, 3
+				      or 4.
+	* 1st and 2nd cells represent the frequency range (min-max).
+	* 3rd cell is optional and represents the OUT field value for the given
+	  range.
+	* 4th cell is optional and represents the ICPLL field (PLLICPR
+	  register)
+- atmel,pll-clk-output-ranges : pll output frequency ranges + optional parameter
+				depending on #atmel,pll-output-range-cells
+				property value.
+
+For example:
+	plla: pllack {
+		compatible = "atmel,at91sam9g45-clk-pll";
+		interrupt-parent = <&pmc>;
+		interrupts = <1>;
+		#clock-cells = <0>;
+		clocks = <&main>;
+		reg = <0>;
+		atmel,clk-input-range = <2000000 32000000>;
+		#atmel,pll-clk-output-range-cells = <4>;
+		atmel,pll-clk-output-ranges = <74500000 800000000 0 0
+					       69500000 750000000 1 0
+					       64500000 700000000 2 0
+					       59500000 650000000 3 0
+					       54500000 600000000 0 1
+					       49500000 550000000 1 1
+					       44500000 500000000 2 1
+					       40000000 450000000 3 1>;
+	};
+
+Required properties for plldiv clocks (plldiv = pll / 2):
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : shall be the plla clock phandle.
+
+The pll divisor is equal to 2 and cannot be changed.
+
+For example:
+	plladiv: plladivck {
+		compatible = "atmel,at91sam9x5-clk-plldiv";
+		#clock-cells = <0>;
+		clocks = <&plla>;
+	};
+
+Required properties for programmable clocks:
+- interrupt-parent : must reference the PMC node.
+- #size-cells : shall be 0 (reg is used to encode clk id).
+- #address-cells : shall be 1 (reg is used to encode clk id).
+- clocks : shall be the programmable clock source phandles.
+	e.g. clocks = <&clk32k>, <&main>, <&plla>, <&pllb>;
+- name: device tree node describing a specific prog clock.
+	* #clock-cells : from common clock binding; shall be set to 0.
+	* reg : programmable clock id (register offset from  PCKx
+			 register).
+	* interrupts : shall be set to "<(8 + id)>".
+
+For example:
+	prog: progck {
+		compatible = "atmel,at91sam9g45-clk-programmable";
+		#size-cells = <0>;
+		#address-cells = <1>;
+		interrupt-parent = <&pmc>;
+		clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>;
+
+		prog0 {
+			#clock-cells = <0>;
+			reg = <0>;
+			interrupts = <8>;
+		};
+
+		prog1 {
+			#clock-cells = <0>;
+			reg = <1>;
+			interrupts = <9>;
+		};
+	};
+
+
+Required properties for smd clock:
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : shall be the smd clock source phandles.
+	e.g. clocks = <&plladiv>, <&utmi>;
+
+For example:
+	smd: smdck {
+		compatible = "atmel,at91sam9x5-clk-smd";
+		#clock-cells = <0>;
+		clocks = <&plladiv>, <&utmi>;
+	};
+
+Required properties for system clocks:
+- #size-cells : shall be 0 (reg is used to encode clk id).
+- #address-cells : shall be 1 (reg is used to encode clk id).
+- name: device tree node describing a specific system clock.
+	* #clock-cells : from common clock binding; shall be set to 0.
+	* reg: system clock id (bit position in SCER/SCDR/SCSR registers).
+	      See Atmel's datasheet to get a full list of system clock ids.
+
+For example:
+	system: systemck {
+		compatible = "atmel,at91rm9200-clk-system";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ddrck {
+			#clock-cells = <0>;
+			reg = <2>;
+			clocks = <&mck>;
+		};
+
+		uhpck {
+			#clock-cells = <0>;
+			reg = <6>;
+			clocks = <&usb>;
+		};
+
+		udpck {
+			#clock-cells = <0>;
+			reg = <7>;
+			clocks = <&usb>;
+		};
+	};
+
+
+Required properties for usb clock:
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : shall be the smd clock source phandles.
+	e.g. clocks = <&pllb>;
+- atmel,clk-divisors (only available for "atmel,at91rm9200-clk-usb"):
+	usb clock divisor table.
+	e.g. divisors = <1 2 4 0>;
+
+For example:
+	usb: usbck {
+		compatible = "atmel,at91sam9x5-clk-usb";
+		#clock-cells = <0>;
+		clocks = <&plladiv>, <&utmi>;
+	};
+
+	usb: usbck {
+		compatible = "atmel,at91rm9200-clk-usb";
+		#clock-cells = <0>;
+		clocks = <&pllb>;
+		atmel,clk-divisors = <1 2 4 0>;
+	};
+
+
+Required properties for utmi clock:
+- interrupt-parent : must reference the PMC node.
+- interrupts : shall be set to "<AT91_PMC_LOCKU IRQ_TYPE_LEVEL_HIGH>".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : shall be the main clock source phandle.
+
+For example:
+	utmi: utmick {
+		compatible = "atmel,at91sam9x5-clk-utmi";
+		interrupt-parent = <&pmc>;
+		interrupts = <AT91_PMC_LOCKU IRQ_TYPE_LEVEL_HIGH>;
+		#clock-cells = <0>;
+		clocks = <&main>;
+	};
-- 
1.7.9.5


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

* [PATCH v5 16/17] ARM: at91: move pit timer to common clk framework
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (14 preceding siblings ...)
  2013-11-13  7:12 ` [PATCH v5 15/17] dt: binding: add at91 clks dt bindings documentation Boris BREZILLON
@ 2013-11-13  7:15 ` Boris BREZILLON
  2013-11-13  7:18 ` [PATCH v5 17/17] ARM: at91: add new compatible strings for pmc driver Boris BREZILLON
  2013-11-30 22:56 ` [PATCH v5 00/17] ARM: at91: move to common clk framework Mike Turquette
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-13  7:15 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

Use device tree to get the source clock of the PIT (Periodic Interval Timer).
If the clock is not found in device tree (or dt is not enabled) we'll try to
get it using clk_lookup definitions.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/at91sam926x_time.c |   14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c
index bb39232..0f04ffe 100644
--- a/arch/arm/mach-at91/at91sam926x_time.c
+++ b/arch/arm/mach-at91/at91sam926x_time.c
@@ -39,6 +39,7 @@
 static u32 pit_cycle;		/* write-once */
 static u32 pit_cnt;		/* access only w/system irq blocked */
 static void __iomem *pit_base_addr __read_mostly;
+static struct clk *mck;
 
 static inline unsigned int pit_read(unsigned int reg_offset)
 {
@@ -195,10 +196,14 @@ static int __init of_at91sam926x_pit_init(void)
 	if (!pit_base_addr)
 		goto node_err;
 
+	mck = of_clk_get(np, 0);
+
 	/* Get the interrupts property */
 	ret = irq_of_parse_and_map(np, 0);
 	if (!ret) {
 		pr_crit("AT91: PIT: Unable to get IRQ from DT\n");
+		if (!IS_ERR(mck))
+			clk_put(mck);
 		goto ioremap_err;
 	}
 	at91sam926x_pit_irq.irq = ret;
@@ -230,6 +235,8 @@ void __init at91sam926x_pit_init(void)
 	unsigned	bits;
 	int		ret;
 
+	mck = ERR_PTR(-ENOENT);
+
 	/* For device tree enabled device: initialize here */
 	of_at91sam926x_pit_init();
 
@@ -237,7 +244,12 @@ void __init at91sam926x_pit_init(void)
 	 * Use our actual MCK to figure out how many MCK/16 ticks per
 	 * 1/HZ period (instead of a compile-time constant LATCH).
 	 */
-	pit_rate = clk_get_rate(clk_get(NULL, "mck")) / 16;
+	if (IS_ERR(mck))
+		mck = clk_get(NULL, "mck");
+
+	if (IS_ERR(mck))
+		panic("AT91: PIT: Unable to get mck clk\n");
+	pit_rate = clk_get_rate(mck) / 16;
 	pit_cycle = (pit_rate + HZ/2) / HZ;
 	WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0);
 
-- 
1.7.9.5


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

* [PATCH v5 17/17] ARM: at91: add new compatible strings for pmc driver
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (15 preceding siblings ...)
  2013-11-13  7:15 ` [PATCH v5 16/17] ARM: at91: move pit timer to common clk framework Boris BREZILLON
@ 2013-11-13  7:18 ` Boris BREZILLON
  2013-11-30 22:56 ` [PATCH v5 00/17] ARM: at91: move to common clk framework Mike Turquette
  17 siblings, 0 replies; 22+ messages in thread
From: Boris BREZILLON @ 2013-11-13  7:18 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Rob Landley, Andrew Victor, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Russell King, Mike Turquette,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

This patch adds new compatible string for PMC node to prepare the
transition to common clk.

These compatible string come from pmc driver in clk subsystem and are
needed to provide new device tree compatibility with old at91 clks
(device tree using common clks will use the new compatible strings).

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/clock.c |    5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c
index 5f02aea..72b2579 100644
--- a/arch/arm/mach-at91/clock.c
+++ b/arch/arm/mach-at91/clock.c
@@ -884,6 +884,11 @@ static int __init at91_pmc_init(unsigned long main_clock)
 #if defined(CONFIG_OF)
 static struct of_device_id pmc_ids[] = {
 	{ .compatible = "atmel,at91rm9200-pmc" },
+	{ .compatible = "atmel,at91sam9260-pmc" },
+	{ .compatible = "atmel,at91sam9g45-pmc" },
+	{ .compatible = "atmel,at91sam9n12-pmc" },
+	{ .compatible = "atmel,at91sam9x5-pmc" },
+	{ .compatible = "atmel,sama5d3-pmc" },
 	{ /*sentinel*/ }
 };
 
-- 
1.7.9.5


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

* Re: [PATCH v5 06/17] clk: at91: add PMC pll clocks
  2013-11-12 21:57 ` [PATCH v5 06/17] clk: at91: add PMC pll clocks Boris BREZILLON
@ 2013-11-27 21:48   ` Mike Turquette
  2013-11-28  8:34     ` boris brezillon
  0 siblings, 1 reply; 22+ messages in thread
From: Mike Turquette @ 2013-11-27 21:48 UTC (permalink / raw)
  To: Boris BREZILLON, Rob Herring, Pawel Moll, Mark Rutland,
	Stephen Warren, Ian Campbell, Rob Landley, Andrew Victor,
	Nicolas Ferre, Jean-Christophe Plagniol-Villard, Russell King,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

Quoting Boris BREZILLON (2013-11-12 13:57:19)
> +static const struct clk_ops pll_ops = {
> +       .prepare = clk_pll_prepare,
> +       .is_prepared = clk_pll_is_ready,
> +       .disable = clk_pll_disable,
> +       .is_enabled = clk_pll_is_ready,
> +       .recalc_rate = clk_pll_recalc_rate,
> +       .round_rate = clk_pll_round_rate,
> +       .set_rate = clk_pll_set_rate,
> +};

Hi Boris,

It is a bit strange to see only a .prepare and .disable callback
populated. What happens if a driver calls clk_disable and then
clk_enable? You clock will still be disabled in hardware.

Regards,
Mike

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

* Re: [PATCH v5 08/17] clk: at91: add PMC system clocks
  2013-11-12 22:05 ` [PATCH v5 08/17] clk: at91: add PMC system clocks Boris BREZILLON
@ 2013-11-27 22:05   ` Mike Turquette
  0 siblings, 0 replies; 22+ messages in thread
From: Mike Turquette @ 2013-11-27 22:05 UTC (permalink / raw)
  To: Boris BREZILLON, Rob Herring, Pawel Moll, Mark Rutland,
	Stephen Warren, Ian Campbell, Rob Landley, Andrew Victor,
	Nicolas Ferre, Jean-Christophe Plagniol-Villard, Russell King,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

Quoting Boris BREZILLON (2013-11-12 14:05:35)
> This patch adds new at91 system clock implementation using common clk
> framework.
> 
> Some peripherals need to enable a "system" clock in order to work properly.
> Each system clock is given an id based on the bit position in SCER/SCDR
> registers.
> 
> Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
> Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> ---
>  drivers/clk/at91/Makefile     |    1 +
>  drivers/clk/at91/clk-system.c |  137 +++++++++++++++++++++++++++++++++++++++++
>  drivers/clk/at91/pmc.c        |    5 ++
>  drivers/clk/at91/pmc.h        |    3 +
>  4 files changed, 146 insertions(+)
>  create mode 100644 drivers/clk/at91/clk-system.c
> 
> diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
> index e28fb2b..c2b7068 100644
> --- a/drivers/clk/at91/Makefile
> +++ b/drivers/clk/at91/Makefile
> @@ -4,3 +4,4 @@
>  
>  obj-y += pmc.o
>  obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o
> +obj-y += clk-system.o
> diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
> new file mode 100644
> index 0000000..1c086f4
> --- /dev/null
> +++ b/drivers/clk/at91/clk-system.c
> @@ -0,0 +1,137 @@
> +/*
> + * drivers/clk/at91/clk-system.c

Nitpick: it's better to not put file paths in a comment block in case
the file name or path is changed.

Regards,
Mike

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

* Re: [PATCH v5 06/17] clk: at91: add PMC pll clocks
  2013-11-27 21:48   ` Mike Turquette
@ 2013-11-28  8:34     ` boris brezillon
  0 siblings, 0 replies; 22+ messages in thread
From: boris brezillon @ 2013-11-28  8:34 UTC (permalink / raw)
  To: Mike Turquette, Rob Herring, Pawel Moll, Mark Rutland,
	Stephen Warren, Ian Campbell, Rob Landley, Andrew Victor,
	Nicolas Ferre, Jean-Christophe Plagniol-Villard, Russell King,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: devicetree, linux-doc, linux-kernel, linux-arm-kernel, linux-usb

Hi Mike,

On 27/11/2013 22:48, Mike Turquette wrote:
> Quoting Boris BREZILLON (2013-11-12 13:57:19)
>> +static const struct clk_ops pll_ops = {
>> +       .prepare = clk_pll_prepare,
>> +       .is_prepared = clk_pll_is_ready,
>> +       .disable = clk_pll_disable,
>> +       .is_enabled = clk_pll_is_ready,
>> +       .recalc_rate = clk_pll_recalc_rate,
>> +       .round_rate = clk_pll_round_rate,
>> +       .set_rate = clk_pll_set_rate,
>> +};
> Hi Boris,
>
> It is a bit strange to see only a .prepare and .disable callback
> populated. What happens if a driver calls clk_disable and then
> clk_enable? You clock will still be disabled in hardware.

Oops, I haven't thought about that.
I did this because the clk_pll_disable does not sleep, but this is a big 
mistake :-(.

I'll fix this.

Thanks.

Best Regards,

Boris
> Regards,
> Mike


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

* Re: [PATCH v5 00/17] ARM: at91: move to common clk framework
  2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
                   ` (16 preceding siblings ...)
  2013-11-13  7:18 ` [PATCH v5 17/17] ARM: at91: add new compatible strings for pmc driver Boris BREZILLON
@ 2013-11-30 22:56 ` Mike Turquette
  17 siblings, 0 replies; 22+ messages in thread
From: Mike Turquette @ 2013-11-30 22:56 UTC (permalink / raw)
  To: Boris BREZILLON, Rob Herring, Pawel Moll, Mark Rutland,
	Stephen Warren, Ian Campbell, Rob Landley, Andrew Victor,
	Nicolas Ferre, Jean-Christophe Plagniol-Villard, Russell King,
	Felipe Balbi, Greg Kroah-Hartman, Grant Likely,
	Ludovic Desroches, Josh Wu, Richard Genoud
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-usb

Quoting Boris BREZILLON (2013-11-12 12:51:52)
> Hello,
> 
> This patch series is the 5th version of the at91 clk implementations
> using the Common Clk Framework.

Thanks for sending this latest version. A couple recurring themes to be
addressed:

1) Some clocks implement .prepare and .disable but not .unprepre nor
.enable. Please only use .prepare and .unprepare in this case.

2) Remove the file names & paths from the comment block at the top of
the files.

These are minor issues. Are you taking these through your platform tree or the
clk tree? If you want to take them through your platform tree then feel
free to add my acked-by to the next version.

Regards,
Mike

> 
> Most of the clock provided by the PMC (Power Management Controller) are
> implemented :
> - main clock (main oscillator)
> - pll clocks
> - master clock
> - programmable clocks
> - utmi clock
> - peripheral clocks
> - system clocks
> 
> Actually some clk drivers are missing (slow clk, main clk and processor
> clk), and some drivers only implement a subset of the hardware capabilities
> (master clk rate change is missing).
> But this series implements all the already available clks, and I will hopefully
> add missing features in a near future.
> 
> This implementation is only compatible with device tree definition.
> The goal is to define the whole clock tree using the device tree.
> 
> BTW, could a dt maintainer take a look at these dt bindinds ?
> 
> Just a note to let you know these bindings are currently unstable and might
> change in the future.
> 
> 
> Mike, I know you already acked the previous version, but I slighty changed
> the dt bindings, according to some feedbacks I got during ELCE.
> Could you take a look at this new version (at least the new dt bindings),
> and add your acked-by if you agree with these changes ?
> 
> Best Regards,
> Boris
> 
> Changes since v4:
>  - rework dt bindings:
>    * replace "atmel,clk-id" property by the standard "reg" property
>    * reference system, peripheral and programmable clks using the direct
>      clk node instead of the parent node plus a clk id
>  - provide a new helper function (of_at91_get_clk_range) to retrieve a clk
>    range from the device tree
> 
> Changes since v3:
>  - simplify master clk implementation (drop set_rate/parent support)
>  - fix bug in set_rate function of pll driver
>  - fix coding style issues
>  - define macros and constants where needed
>  - remove peripheral id macro references
>  - remove sam9g35 specific handling (sam9g35 = sam9x5)
>  - rework main clk prepare function to handle automatic rate calculation
> 
> Changes since v2:
>  - fix several bugs in clk implementations
>  - drop non-dt boards support
>  - split the series to ease review and tests:
>    * 1 patch series for new clk implementations (this series)
>    * 1 patch series to move each at91 SoC to common clk framework (coming soon)
>  - modify dt-bindings (add atmel,clk- prefix to atmel specific properties)
>  - add clk macros for dt-bindings
>  - add pmc framework (helper function to access pmc registers)
>  - add interrupt support to enable passive wait in clk_prepare functions
> 
> Changes since v1:
>  - fix bugs in pll, programmable and system clock implementations
>    (wrong bit position).
>  - add usb clock configuration support (ohci and udc drivers +
>    clk_lookup for non dt boards)
>  - rework of the system clock interfaces (no need to define a parent clock,
>    system clock is a gate with no rate info)
>  - change system, peripheral and programmable clk dt bindings (1 master node
>    and multiple child nodes each defining a system/peripheral or prog clock)
>  - fix bugs in sama5 dt definition
> 
> Boris BREZILLON (17):
>   ARM: at91: move at91_pmc.h to include/linux/clk/at91_pmc.h
>   ARM: at91: add Kconfig options for common clk support
>   clk: at91: add PMC base support
>   clk: at91: add PMC macro file for dt definitions
>   clk: at91: add PMC main clock
>   clk: at91: add PMC pll clocks
>   clk: at91: add PMC master clock
>   clk: at91: add PMC system clocks
>   clk: at91: add PMC peripheral clocks
>   clk: at91: add peripheral clk macros for peripheral clk dt bindings
>   clk: at91: add PMC programmable clocks
>   clk: at91: add PMC utmi clock
>   clk: at91: add PMC usb clock
>   clk: at91: add PMC smd clock
>   clk: at91: add PMC clk device tree binding doc.
>   ARM: at91: move pit timer to common clk framework
>   ARM: at91: add new compatible strings for pmc driver
> 
>  .../devicetree/bindings/clock/at91-clock.txt       |  339 +++++++++++++
>  arch/arm/mach-at91/Kconfig                         |   44 ++
>  arch/arm/mach-at91/Kconfig.non_dt                  |    6 +
>  arch/arm/mach-at91/Makefile                        |    2 +-
>  arch/arm/mach-at91/at91rm9200.c                    |    2 +-
>  arch/arm/mach-at91/at91sam9260.c                   |    2 +-
>  arch/arm/mach-at91/at91sam9261.c                   |    2 +-
>  arch/arm/mach-at91/at91sam9263.c                   |    2 +-
>  arch/arm/mach-at91/at91sam926x_time.c              |   14 +-
>  arch/arm/mach-at91/at91sam9g45.c                   |    2 +-
>  arch/arm/mach-at91/at91sam9n12.c                   |    2 +-
>  arch/arm/mach-at91/at91sam9rl.c                    |    2 +-
>  arch/arm/mach-at91/at91sam9x5.c                    |    2 +-
>  arch/arm/mach-at91/clock.c                         |    7 +-
>  arch/arm/mach-at91/generic.h                       |    3 +-
>  arch/arm/mach-at91/pm.c                            |    2 +-
>  arch/arm/mach-at91/pm_slowclock.S                  |    2 +-
>  arch/arm/mach-at91/sama5d3.c                       |    2 +-
>  arch/arm/mach-at91/setup.c                         |    8 +-
>  drivers/clk/Makefile                               |    1 +
>  drivers/clk/at91/Makefile                          |   12 +
>  drivers/clk/at91/clk-main.c                        |  189 +++++++
>  drivers/clk/at91/clk-master.c                      |  272 ++++++++++
>  drivers/clk/at91/clk-peripheral.c                  |  412 +++++++++++++++
>  drivers/clk/at91/clk-pll.c                         |  534 ++++++++++++++++++++
>  drivers/clk/at91/clk-plldiv.c                      |  137 +++++
>  drivers/clk/at91/clk-programmable.c                |  368 ++++++++++++++
>  drivers/clk/at91/clk-smd.c                         |  173 +++++++
>  drivers/clk/at91/clk-system.c                      |  137 +++++
>  drivers/clk/at91/clk-usb.c                         |  400 +++++++++++++++
>  drivers/clk/at91/clk-utmi.c                        |  162 ++++++
>  drivers/clk/at91/pmc.c                             |  399 +++++++++++++++
>  drivers/clk/at91/pmc.h                             |  116 +++++
>  drivers/usb/gadget/atmel_usba_udc.c                |    2 +-
>  include/dt-bindings/clk/at91.h                     |   28 +
>  .../include/mach => include/linux/clk}/at91_pmc.h  |    4 +-
>  36 files changed, 3771 insertions(+), 20 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/clock/at91-clock.txt
>  create mode 100644 drivers/clk/at91/Makefile
>  create mode 100644 drivers/clk/at91/clk-main.c
>  create mode 100644 drivers/clk/at91/clk-master.c
>  create mode 100644 drivers/clk/at91/clk-peripheral.c
>  create mode 100644 drivers/clk/at91/clk-pll.c
>  create mode 100644 drivers/clk/at91/clk-plldiv.c
>  create mode 100644 drivers/clk/at91/clk-programmable.c
>  create mode 100644 drivers/clk/at91/clk-smd.c
>  create mode 100644 drivers/clk/at91/clk-system.c
>  create mode 100644 drivers/clk/at91/clk-usb.c
>  create mode 100644 drivers/clk/at91/clk-utmi.c
>  create mode 100644 drivers/clk/at91/pmc.c
>  create mode 100644 drivers/clk/at91/pmc.h
>  create mode 100644 include/dt-bindings/clk/at91.h
>  rename {arch/arm/mach-at91/include/mach => include/linux/clk}/at91_pmc.h (98%)
> 
> -- 
> 1.7.9.5

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

end of thread, other threads:[~2013-11-30 23:02 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-12 20:51 [PATCH v5 00/17] ARM: at91: move to common clk framework Boris BREZILLON
2013-11-12 20:55 ` [PATCH v5 01/17] ARM: at91: move at91_pmc.h to include/linux/clk/at91_pmc.h Boris BREZILLON
2013-11-12 20:57 ` [PATCH v5 02/17] ARM: at91: add Kconfig options for common clk support Boris BREZILLON
2013-11-12 21:02 ` [PATCH v5 03/17] clk: at91: add PMC base support Boris BREZILLON
2013-11-12 21:04 ` [PATCH v5 04/17] clk: at91: add PMC macro file for dt definitions Boris BREZILLON
2013-11-12 21:54 ` [PATCH v5 05/17] clk: at91: add PMC main clock Boris BREZILLON
2013-11-12 21:57 ` [PATCH v5 06/17] clk: at91: add PMC pll clocks Boris BREZILLON
2013-11-27 21:48   ` Mike Turquette
2013-11-28  8:34     ` boris brezillon
2013-11-12 22:00 ` [PATCH v5 07/17] clk: at91: add PMC master clock Boris BREZILLON
2013-11-12 22:05 ` [PATCH v5 08/17] clk: at91: add PMC system clocks Boris BREZILLON
2013-11-27 22:05   ` Mike Turquette
2013-11-12 22:08 ` [PATCH v5 09/17] clk: at91: add PMC peripheral clocks Boris BREZILLON
2013-11-13  6:10 ` [PATCH v5 10/17] clk: at91: add peripheral clk macros for peripheral clk dt bindings Boris BREZILLON
2013-11-13  6:14 ` [PATCH v5 11/17] clk: at91: add PMC programmable clocks Boris BREZILLON
2013-11-13  6:17 ` [PATCH v5 12/17] clk: at91: add PMC utmi clock Boris BREZILLON
2013-11-13  6:20 ` [PATCH v5 13/17] clk: at91: add PMC usb clock Boris BREZILLON
2013-11-13  6:22 ` [PATCH v5 14/17] clk: at91: add PMC smd clock Boris BREZILLON
2013-11-13  7:12 ` [PATCH v5 15/17] dt: binding: add at91 clks dt bindings documentation Boris BREZILLON
2013-11-13  7:15 ` [PATCH v5 16/17] ARM: at91: move pit timer to common clk framework Boris BREZILLON
2013-11-13  7:18 ` [PATCH v5 17/17] ARM: at91: add new compatible strings for pmc driver Boris BREZILLON
2013-11-30 22:56 ` [PATCH v5 00/17] ARM: at91: move to common clk framework Mike Turquette

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