[PATCH v5 11/15] h8300: clock driver

2015-02-21 Thread Yoshinori Sato
Signed-off-by: Yoshinori Sato 
---
 drivers/clk/Makefile|   1 +
 drivers/clk/h8300/Makefile  |   2 +
 drivers/clk/h8300/clk-h83069.c  |  74 ++
 drivers/clk/h8300/clk-h8s2678.c | 165 
 include/linux/clk-provider.h|  12 +++
 5 files changed, 254 insertions(+)
 create mode 100644 drivers/clk/h8300/Makefile
 create mode 100644 drivers/clk/h8300/clk-h83069.c
 create mode 100644 drivers/clk/h8300/clk-h8s2678.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d5fba5b..37b6e87 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -68,3 +68,4 @@ obj-$(CONFIG_ARCH_U8500)  += ux500/
 obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
 obj-$(CONFIG_X86)  += x86/
 obj-$(CONFIG_ARCH_ZYNQ)+= zynq/
+obj-$(CONFIG_H8300)+= h8300/
diff --git a/drivers/clk/h8300/Makefile b/drivers/clk/h8300/Makefile
new file mode 100644
index 000..82eab42
--- /dev/null
+++ b/drivers/clk/h8300/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_H83069) += clk-h83069.o
+obj-$(CONFIG_H8S2678) += clk-h8s2678.o
diff --git a/drivers/clk/h8300/clk-h83069.c b/drivers/clk/h8300/clk-h83069.c
new file mode 100644
index 000..8d1b915
--- /dev/null
+++ b/drivers/clk/h8300/clk-h83069.c
@@ -0,0 +1,74 @@
+/*
+ * H8/3069 clock driver
+ *
+ * Copyright 2015 Yoshinori Sato 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static DEFINE_SPINLOCK(clklock);
+
+#define DIVCR ((unsigned char *)0xfee01b)
+#define DEVNAME "h83069-cpg"
+
+static int clk_probe(struct platform_device *pdev)
+{
+   struct clk *clk;
+   int *hz = dev_get_platdata(>dev);
+
+   clk = clk_register_fixed_rate(>dev, "master_clk", NULL,
+ CLK_IS_ROOT, *hz);
+   if (IS_ERR(clk)) {
+   dev_err(>dev, "failed to register clock");
+   return PTR_ERR(clk);
+   }
+   clk_register_clkdev(clk, "master_clk", DEVNAME ".%d", 0);
+
+   clk = clk_register_divider(>dev, "core_clk", "master_clk",
+  CLK_SET_RATE_GATE, DIVCR, 0, 2,
+  CLK_DIVIDER_POWER_OF_TWO, );
+   if (IS_ERR(clk)) {
+   dev_err(>dev, "failed to register clock");
+   return PTR_ERR(clk);
+   }
+   clk_register_clkdev(clk, "core_clk", DEVNAME ".%d", 0);
+
+   clk_add_alias("peripheral_clk", NULL, "core_clk", >dev);
+   return 0;
+}
+
+static struct platform_driver cpg_driver = {
+   .driver = {
+   .name = DEVNAME,
+   },
+   .probe = clk_probe,
+};
+
+early_platform_init(DEVNAME, _driver);
+
+static struct platform_device clk_device = {
+   .name   = DEVNAME,
+   .id = 0,
+};
+
+static struct platform_device *devices[] __initdata = {
+   _device,
+};
+
+int __init h8300_clk_init(int hz)
+{
+   static int master_hz;
+
+   master_hz = hz;
+   clk_device.dev.platform_data = _hz;
+   early_platform_add_devices(devices,
+  ARRAY_SIZE(devices));
+   early_platform_driver_register_all(DEVNAME);
+   early_platform_driver_probe(DEVNAME, 1, 0);
+   return 0;
+}
diff --git a/drivers/clk/h8300/clk-h8s2678.c b/drivers/clk/h8300/clk-h8s2678.c
new file mode 100644
index 000..e110cff
--- /dev/null
+++ b/drivers/clk/h8300/clk-h8s2678.c
@@ -0,0 +1,165 @@
+/*
+ * H8S2678 clock driver
+ *
+ * Copyright 2015 Yoshinori Sato 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static DEFINE_SPINLOCK(clklock);
+
+#define SCKCR 0x3b
+#define PLLCR 0x45
+#define DEVNAME "h8s2679-cpg"
+#define MAX_FREQ 
+#define MIN_FREQ  800
+
+static unsigned long pll_recalc_rate(struct clk_hw *hw,
+   unsigned long parent_rate)
+{
+   int mul = 1 << (ctrl_inb(PLLCR) & 3);
+
+   return parent_rate * mul;
+}
+
+static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
+   unsigned long *prate)
+{
+   int i, m = -1;
+   long offset[3];
+
+   if (rate > MAX_FREQ)
+   rate = MAX_FREQ;
+   if (rate < MIN_FREQ)
+   rate = MIN_FREQ;
+
+   for (i = 0; i < 3; i++)
+   offset[i] = abs(rate - (*prate * (1 << i)));
+   for (i = 0; i < 3; i++)
+   if (m < 0)
+   m = i;
+   else
+   m = (offset[i] < offset[m])?i:m;
+
+   return *prate * (1 << m);
+}
+
+static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
+   unsigned long parent_rate)
+{
+   int pll;
+   unsigned char val;
+   unsigned long flags;
+
+   pll = ((rate / parent_rate) / 2) & 0x03;
+   spin_lock_irqsave(, flags);
+   val = ctrl_inb(SCKCR);
+   val |= 0x08;
+   ctrl_outb(val, SCKCR);
+   val = ctrl_inb(PLLCR);
+   val &= ~0x03;
+   val |= 

[PATCH v5 11/15] h8300: clock driver

2015-02-21 Thread Yoshinori Sato
Signed-off-by: Yoshinori Sato ys...@users.sourceforge.jp
---
 drivers/clk/Makefile|   1 +
 drivers/clk/h8300/Makefile  |   2 +
 drivers/clk/h8300/clk-h83069.c  |  74 ++
 drivers/clk/h8300/clk-h8s2678.c | 165 
 include/linux/clk-provider.h|  12 +++
 5 files changed, 254 insertions(+)
 create mode 100644 drivers/clk/h8300/Makefile
 create mode 100644 drivers/clk/h8300/clk-h83069.c
 create mode 100644 drivers/clk/h8300/clk-h8s2678.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d5fba5b..37b6e87 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -68,3 +68,4 @@ obj-$(CONFIG_ARCH_U8500)  += ux500/
 obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
 obj-$(CONFIG_X86)  += x86/
 obj-$(CONFIG_ARCH_ZYNQ)+= zynq/
+obj-$(CONFIG_H8300)+= h8300/
diff --git a/drivers/clk/h8300/Makefile b/drivers/clk/h8300/Makefile
new file mode 100644
index 000..82eab42
--- /dev/null
+++ b/drivers/clk/h8300/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_H83069) += clk-h83069.o
+obj-$(CONFIG_H8S2678) += clk-h8s2678.o
diff --git a/drivers/clk/h8300/clk-h83069.c b/drivers/clk/h8300/clk-h83069.c
new file mode 100644
index 000..8d1b915
--- /dev/null
+++ b/drivers/clk/h8300/clk-h83069.c
@@ -0,0 +1,74 @@
+/*
+ * H8/3069 clock driver
+ *
+ * Copyright 2015 Yoshinori Sato ys...@users.sourceforge.jp
+ */
+
+#include linux/clk.h
+#include linux/clkdev.h
+#include linux/clk-provider.h
+#include linux/err.h
+#include linux/device.h
+#include linux/platform_device.h
+
+static DEFINE_SPINLOCK(clklock);
+
+#define DIVCR ((unsigned char *)0xfee01b)
+#define DEVNAME h83069-cpg
+
+static int clk_probe(struct platform_device *pdev)
+{
+   struct clk *clk;
+   int *hz = dev_get_platdata(pdev-dev);
+
+   clk = clk_register_fixed_rate(pdev-dev, master_clk, NULL,
+ CLK_IS_ROOT, *hz);
+   if (IS_ERR(clk)) {
+   dev_err(pdev-dev, failed to register clock);
+   return PTR_ERR(clk);
+   }
+   clk_register_clkdev(clk, master_clk, DEVNAME .%d, 0);
+
+   clk = clk_register_divider(pdev-dev, core_clk, master_clk,
+  CLK_SET_RATE_GATE, DIVCR, 0, 2,
+  CLK_DIVIDER_POWER_OF_TWO, clklock);
+   if (IS_ERR(clk)) {
+   dev_err(pdev-dev, failed to register clock);
+   return PTR_ERR(clk);
+   }
+   clk_register_clkdev(clk, core_clk, DEVNAME .%d, 0);
+
+   clk_add_alias(peripheral_clk, NULL, core_clk, pdev-dev);
+   return 0;
+}
+
+static struct platform_driver cpg_driver = {
+   .driver = {
+   .name = DEVNAME,
+   },
+   .probe = clk_probe,
+};
+
+early_platform_init(DEVNAME, cpg_driver);
+
+static struct platform_device clk_device = {
+   .name   = DEVNAME,
+   .id = 0,
+};
+
+static struct platform_device *devices[] __initdata = {
+   clk_device,
+};
+
+int __init h8300_clk_init(int hz)
+{
+   static int master_hz;
+
+   master_hz = hz;
+   clk_device.dev.platform_data = master_hz;
+   early_platform_add_devices(devices,
+  ARRAY_SIZE(devices));
+   early_platform_driver_register_all(DEVNAME);
+   early_platform_driver_probe(DEVNAME, 1, 0);
+   return 0;
+}
diff --git a/drivers/clk/h8300/clk-h8s2678.c b/drivers/clk/h8300/clk-h8s2678.c
new file mode 100644
index 000..e110cff
--- /dev/null
+++ b/drivers/clk/h8300/clk-h8s2678.c
@@ -0,0 +1,165 @@
+/*
+ * H8S2678 clock driver
+ *
+ * Copyright 2015 Yoshinori Sato ys...@users.sourceforge.jp
+ */
+
+#include linux/clk.h
+#include linux/clkdev.h
+#include linux/clk-provider.h
+#include linux/err.h
+#include linux/device.h
+#include linux/platform_device.h
+
+static DEFINE_SPINLOCK(clklock);
+
+#define SCKCR 0x3b
+#define PLLCR 0x45
+#define DEVNAME h8s2679-cpg
+#define MAX_FREQ 
+#define MIN_FREQ  800
+
+static unsigned long pll_recalc_rate(struct clk_hw *hw,
+   unsigned long parent_rate)
+{
+   int mul = 1  (ctrl_inb(PLLCR)  3);
+
+   return parent_rate * mul;
+}
+
+static long pll_round_rate(struct clk_hw *hw, unsigned long rate,
+   unsigned long *prate)
+{
+   int i, m = -1;
+   long offset[3];
+
+   if (rate  MAX_FREQ)
+   rate = MAX_FREQ;
+   if (rate  MIN_FREQ)
+   rate = MIN_FREQ;
+
+   for (i = 0; i  3; i++)
+   offset[i] = abs(rate - (*prate * (1  i)));
+   for (i = 0; i  3; i++)
+   if (m  0)
+   m = i;
+   else
+   m = (offset[i]  offset[m])?i:m;
+
+   return *prate * (1  m);
+}
+
+static int pll_set_rate(struct clk_hw *hw, unsigned long rate,
+   unsigned long parent_rate)
+{
+   int pll;
+   unsigned char val;
+