Re: [PATCH v4 07/17] clk: at91: add PMC master clock

2013-10-17 Thread Nicolas Ferre

On 11/10/2013 10:51, Boris BREZILLON :

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 


Acked-by: Nicolas Ferre 


---
  drivers/clk/at91/Makefile |2 +-
  drivers/clk/at91/clk-master.c |  278 +
  drivers/clk/at91/clk-pll.c|   10 +-
  drivers/clk/at91/pmc.c|9 ++
  drivers/clk/at91/pmc.h|5 +
  5 files changed, 295 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 000..58af23d
--- /dev/null
+++ b/drivers/clk/at91/clk-master.c
@@ -0,0 +1,278 @@
+/*
+ * drivers/clk/at91/clk-master.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON 
+ *
+ * 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "pmc.h"
+
+#define MASTER_SOURCE_MAX  4
+
+#define MASTER_PRES_MASK   0x7
+#define MASTER_PRES_MAXMASTER_PRES_MASK
+#define MASTER_DIV_SHIFT   8
+#define MASTER_DIV_MASK0x3
+
+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(>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,
+   

Re: [PATCH v4 07/17] clk: at91: add PMC master clock

2013-10-17 Thread Nicolas Ferre

On 11/10/2013 10:51, Boris BREZILLON :

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.brezil...@overkiz.com


Acked-by: Nicolas Ferre nicolas.fe...@atmel.com


---
  drivers/clk/at91/Makefile |2 +-
  drivers/clk/at91/clk-master.c |  278 +
  drivers/clk/at91/clk-pll.c|   10 +-
  drivers/clk/at91/pmc.c|9 ++
  drivers/clk/at91/pmc.h|5 +
  5 files changed, 295 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 000..58af23d
--- /dev/null
+++ b/drivers/clk/at91/clk-master.c
@@ -0,0 +1,278 @@
+/*
+ * drivers/clk/at91/clk-master.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON b.brezil...@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_MAXMASTER_PRES_MASK
+#define MASTER_DIV_SHIFT   8
+#define MASTER_DIV_MASK0x3
+
+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 

[PATCH v4 07/17] clk: at91: add PMC master clock

2013-10-11 Thread Boris BREZILLON
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 
---
 drivers/clk/at91/Makefile |2 +-
 drivers/clk/at91/clk-master.c |  278 +
 drivers/clk/at91/clk-pll.c|   10 +-
 drivers/clk/at91/pmc.c|9 ++
 drivers/clk/at91/pmc.h|5 +
 5 files changed, 295 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 000..58af23d
--- /dev/null
+++ b/drivers/clk/at91/clk-master.c
@@ -0,0 +1,278 @@
+/*
+ * drivers/clk/at91/clk-master.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON 
+ *
+ * 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "pmc.h"
+
+#define MASTER_SOURCE_MAX  4
+
+#define MASTER_PRES_MASK   0x7
+#define MASTER_PRES_MAXMASTER_PRES_MASK
+#define MASTER_DIV_SHIFT   8
+#define MASTER_DIV_MASK0x3
+
+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(>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,

[PATCH v4 07/17] clk: at91: add PMC master clock

2013-10-11 Thread Boris BREZILLON
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.brezil...@overkiz.com
---
 drivers/clk/at91/Makefile |2 +-
 drivers/clk/at91/clk-master.c |  278 +
 drivers/clk/at91/clk-pll.c|   10 +-
 drivers/clk/at91/pmc.c|9 ++
 drivers/clk/at91/pmc.h|5 +
 5 files changed, 295 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 000..58af23d
--- /dev/null
+++ b/drivers/clk/at91/clk-master.c
@@ -0,0 +1,278 @@
+/*
+ * drivers/clk/at91/clk-master.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON b.brezil...@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_MAXMASTER_PRES_MASK
+#define MASTER_DIV_SHIFT   8
+#define MASTER_DIV_MASK0x3
+
+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;
+}
+