On 16/05/07 10:19 +0200, Sylvain Munaut wrote:
>  - You do read/write/modify operation on CDM shared register
> (clk_enables) from a driver, you should have added something in common
> 52xx code to do theses with proper locking.
>  - MPC52xx_PA(MPC52xx_PSCx_OFFSET(...)) ??? You should get that from the
> resource of the platform_device. This macro is just there for early
> console stuff.
>  - You can get f_system from the device tree instead of just assuming
> it's 512 MHz. It probably need to be done the same way it's done to find
> ipb_freq.
>  - Would have been nice to be able to somehow configure MCLK rather than
> #define it

[ trimming spi-devel from cc: ]


Use clk.h interface for mpc52xx.
Parse device tree for Fsystem, or leave it default 528 (yes, not 512).

Currently only psc_mclks are defined.


Signed-off-by: Domen Puncer <[EMAIL PROTECTED]>

---
 arch/powerpc/platforms/52xx/Makefile |    2 
 arch/powerpc/platforms/52xx/clock.c  |  311 +++++++++++++++++++++++++++++++++++
 2 files changed, 312 insertions(+), 1 deletion(-)

Index: work-powerpc.git/arch/powerpc/platforms/52xx/Makefile
===================================================================
--- work-powerpc.git.orig/arch/powerpc/platforms/52xx/Makefile
+++ work-powerpc.git/arch/powerpc/platforms/52xx/Makefile
@@ -2,7 +2,7 @@
 # Makefile for 52xx based boards
 #
 ifeq ($(CONFIG_PPC_MERGE),y)
-obj-y                          += mpc52xx_pic.o mpc52xx_common.o
+obj-y                          += mpc52xx_pic.o mpc52xx_common.o clock.o
 obj-$(CONFIG_PCI)              += mpc52xx_pci.o
 obj-$(CONFIG_PPC_BESTCOMM)     += bestcomm.o
 obj-$(CONFIG_FEC_MPC52xx)      += sdma_fec_rx_task.o sdma_fec_tx_task.o fec.o
Index: work-powerpc.git/arch/powerpc/platforms/52xx/clock.c
===================================================================
--- /dev/null
+++ work-powerpc.git/arch/powerpc/platforms/52xx/clock.c
@@ -0,0 +1,311 @@
+/*
+ * arch/powerpc/platforms/52xx/clock.c
+ * based on linux/arch/arm/mach-at91/clock.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+
+
+struct clk {
+       struct list_head node;
+       const char      *name;          /* unique clock name */
+       unsigned long   rate_hz;
+       struct clk      *parent;
+       void            (*mode)(struct clk *, int);
+       u16             users;
+       int             cdm_id;         /* == bit number in datasheet, or 0 */
+       long            (*set_rate)(struct clk *, unsigned long rate, int 
round_only);
+};
+
+#define CDM_ID_PSC3 24
+#define CDM_ID_PSC2 25
+#define CDM_ID_PSC1 26
+#define CDM_ID_PSC6 27
+
+long psc_set_rate(struct clk *clk, unsigned long rate, int round_only);
+
+static struct clk clk_system = {
+       .name           = "system",
+       .rate_hz        = 528000000,    /* default if there's no 
system-frequency in dts */
+       .users          = 1,            /* always on */
+};
+static struct clk clk_psc1 = {
+       .name           = "psc1_mclk",
+       .cdm_id         = CDM_ID_PSC1,
+       .parent         = &clk_system,
+       .set_rate       = psc_set_rate,
+};
+static struct clk clk_psc2 = {
+       .name           = "psc2_mclk",
+       .cdm_id         = CDM_ID_PSC2,
+       .parent         = &clk_system,
+       .set_rate       = psc_set_rate,
+};
+static struct clk clk_psc3 = {
+       .name           = "psc3_mclk",
+       .cdm_id         = CDM_ID_PSC3,
+       .parent         = &clk_system,
+       .set_rate       = psc_set_rate,
+};
+static struct clk clk_psc6 = {
+       .name           = "psc6_mclk",
+       .cdm_id         = CDM_ID_PSC6,
+       .parent         = &clk_system,
+       .set_rate       = psc_set_rate,
+};
+
+
+
+static LIST_HEAD(clocks);
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct mpc52xx_cdm __iomem *cdm;
+static DEFINE_SPINLOCK(cdm_lock);
+
+
+long psc_set_rate(struct clk *clk, unsigned long rate, int round_only)
+{
+       u16 mclkdiv;
+       u16 __iomem *divreg;
+
+       /* pick a divider that will get the closest clock */
+       mclkdiv = (clk->parent->rate_hz + rate/2) / rate - 1;
+
+       /* trim to closest possible. or should we return an error? */
+       mclkdiv = min(mclkdiv, (u16)0x1ff);
+       mclkdiv = max(mclkdiv, (u16)1);
+
+       rate = clk->parent->rate_hz / (mclkdiv + 1);
+       mclkdiv |= 0x8000;      /* enable (this is not clk_enable!) */
+
+       if (round_only)
+               return rate;
+
+       if (clk->cdm_id == CDM_ID_PSC1)
+               divreg = &cdm->mclken_div_psc1;
+       else if (clk->cdm_id == CDM_ID_PSC2)
+               divreg = &cdm->mclken_div_psc2;
+       else if (clk->cdm_id == CDM_ID_PSC3)
+               divreg = &cdm->mclken_div_psc3;
+       else if (clk->cdm_id == CDM_ID_PSC6)
+               divreg = &cdm->mclken_div_psc6;
+       else
+               return -ENODEV;
+
+       out_be16(divreg, mclkdiv);
+
+       return 0;
+}
+
+/* clocks cannot be de-registered no refcounting necessary */
+struct clk *clk_get(struct device *dev, const char *id)
+{
+       struct clk *clk;
+
+       list_for_each_entry(clk, &clocks, node) {
+               if (strcmp(id, clk->name) == 0)
+                       return clk;
+       }
+
+       return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_put);
+
+
+static void clk_mode_cdm(int cdm_id, int enabled)
+{
+       unsigned long flags;
+       u32 clk_enables;
+
+       if (cdm_id < 12 || cdm_id > 31) {
+               printk(KERN_ERR "%s: %i invalid cdm_id: %i\n", __func__, 
__LINE__, cdm_id);
+               return;
+       }
+
+       spin_lock_irqsave(&cdm_lock, flags);
+       clk_enables = in_be32(&cdm->clk_enables);
+       if (enabled)
+               clk_enables |= 1 << (31-cdm_id);
+       else
+               clk_enables &= ~(1 << (31-cdm_id));
+
+       out_be32(&cdm->clk_enables, clk_enables);
+       spin_unlock_irqrestore(&cdm_lock, flags);
+}
+
+static void __clk_enable(struct clk *clk)
+{
+       if (clk->parent)
+               __clk_enable(clk->parent);
+       if (clk->users++ == 0 && clk->mode) {
+               if (clk->cdm_id)
+                       clk_mode_cdm(clk->cdm_id, 1);
+               else
+                       clk->mode(clk, 1);
+       }
+}
+
+int clk_enable(struct clk *clk)
+{
+       unsigned long   flags;
+
+       spin_lock_irqsave(&clk_lock, flags);
+       __clk_enable(clk);
+       spin_unlock_irqrestore(&clk_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+static void __clk_disable(struct clk *clk)
+{
+       BUG_ON(clk->users == 0);
+       if (--clk->users == 0 && clk->mode) {
+               if (clk->cdm_id)
+                       clk_mode_cdm(clk->cdm_id, 0);
+               else
+                       clk->mode(clk, 0);
+       }
+       if (clk->parent)
+               __clk_disable(clk->parent);
+}
+
+void clk_disable(struct clk *clk)
+{
+       unsigned long   flags;
+
+       spin_lock_irqsave(&clk_lock, flags);
+       __clk_disable(clk);
+       spin_unlock_irqrestore(&clk_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+       unsigned long   flags;
+       unsigned long   rate;
+
+       spin_lock_irqsave(&clk_lock, flags);
+       for (;;) {
+               rate = clk->rate_hz;
+               if (rate || !clk->parent)
+                       break;
+               clk = clk->parent;
+       }
+       spin_unlock_irqrestore(&clk_lock, flags);
+       return rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+       unsigned long flags;
+       long ret;
+
+       if (!clk->set_rate)
+               return -EINVAL;
+
+       spin_lock_irqsave(&clk_lock, flags);
+       ret = clk->set_rate(clk, rate, 1);
+       spin_unlock_irqrestore(&clk_lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+       unsigned long flags;
+       int ret;
+
+       if (!clk->set_rate)
+               return -EINVAL;
+       if (clk->users)
+               return -EBUSY;
+
+       spin_lock_irqsave(&clk_lock, flags);
+       clk->rate_hz = clk->set_rate(clk, rate, 1);
+       ret = clk->set_rate(clk, rate, 0);
+       spin_unlock_irqrestore(&clk_lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+       return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+       unsigned long   flags;
+
+       if (clk->users)
+               return -EBUSY;
+       spin_lock_irqsave(&clk_lock, flags);
+
+       clk->rate_hz = parent->rate_hz;
+       clk->parent = parent;
+
+       spin_unlock_irqrestore(&clk_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+
+
+static struct clk *const mpc5200_clocks[] __initdata = {
+       &clk_system,
+       &clk_psc1,
+       &clk_psc2,
+       &clk_psc3,
+       &clk_psc6,
+};
+
+static int __init mpc5200_clock_init(void)
+{
+       int i;
+       unsigned int fsystem = 0;
+       struct device_node *np;
+
+       /* get clk_system rate from device tree */
+       np = of_find_node_by_type(NULL, "soc");
+       if (np) {
+               const unsigned int *fp =
+                       of_get_property(np, "system-frequency", NULL);
+               if (fp)
+                       fsystem = *fp;
+               of_node_put(np);
+       }
+       if (fsystem)
+               clk_system.rate_hz = fsystem;
+
+       cdm = mpc52xx_find_and_map("mpc5200-cdm");
+       if (!cdm) {
+               printk(KERN_ERR "%s: %i couldn't map mpc5200-cdm\n", __func__, 
__LINE__);
+               return -EFAULT;
+       }
+
+       /* Register the PMC's standard clocks */
+       for (i = 0; i < ARRAY_SIZE(mpc5200_clocks); i++)
+               list_add_tail(&mpc5200_clocks[i]->node, &clocks);
+
+       return 0;
+}
+
+arch_initcall(mpc5200_clock_init);
_______________________________________________
Linuxppc-embedded mailing list
Linuxppc-embedded@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-embedded

Reply via email to