| /* | |
| * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved. | |
| * | |
| * Author: John Rigby <jrigby@freescale.com> | |
| * | |
| * Implements the clk api defined in include/linux/clk.h | |
| * | |
| * Original based on linux/arch/arm/mach-integrator/clock.c | |
| * | |
| * Copyright (C) 2004 ARM Limited. | |
| * Written by Deep Blue Solutions Limited. | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License version 2 as | |
| * published by the Free Software Foundation. | |
| */ | |
| #include <linux/kernel.h> | |
| #include <linux/list.h> | |
| #include <linux/errno.h> | |
| #include <linux/err.h> | |
| #include <linux/string.h> | |
| #include <linux/clk.h> | |
| #include <linux/mutex.h> | |
| #include <linux/io.h> | |
| #include <linux/of_platform.h> | |
| #include <asm/mpc512x.h> | |
| #include <asm/clk_interface.h> | |
| #undef CLK_DEBUG | |
| static int clocks_initialized; | |
| #define CLK_HAS_RATE 0x1 /* has rate in MHz */ | |
| #define CLK_HAS_CTRL 0x2 /* has control reg and bit */ | |
| struct clk { | |
| struct list_head node; | |
| char name[32]; | |
| int flags; | |
| struct device *dev; | |
| unsigned long rate; | |
| struct module *owner; | |
| void (*calc) (struct clk *); | |
| struct clk *parent; | |
| int reg, bit; /* CLK_HAS_CTRL */ | |
| int div_shift; /* only used by generic_div_clk_calc */ | |
| }; | |
| static LIST_HEAD(clocks); | |
| static DEFINE_MUTEX(clocks_mutex); | |
| static struct clk *mpc5121_clk_get(struct device *dev, const char *id) | |
| { | |
| struct clk *p, *clk = ERR_PTR(-ENOENT); | |
| int dev_match = 0; | |
| int id_match = 0; | |
| if (dev == NULL || id == NULL) | |
| return NULL; | |
| mutex_lock(&clocks_mutex); | |
| list_for_each_entry(p, &clocks, node) { | |
| if (dev == p->dev) | |
| dev_match++; | |
| if (strcmp(id, p->name) == 0) | |
| id_match++; | |
| if ((dev_match || id_match) && try_module_get(p->owner)) { | |
| clk = p; | |
| break; | |
| } | |
| } | |
| mutex_unlock(&clocks_mutex); | |
| return clk; | |
| } | |
| #ifdef CLK_DEBUG | |
| static void dump_clocks(void) | |
| { | |
| struct clk *p; | |
| mutex_lock(&clocks_mutex); | |
| printk(KERN_INFO "CLOCKS:\n"); | |
| list_for_each_entry(p, &clocks, node) { | |
| printk(KERN_INFO " %s %ld", p->name, p->rate); | |
| if (p->parent) | |
| printk(KERN_INFO " %s %ld", p->parent->name, | |
| p->parent->rate); | |
| if (p->flags & CLK_HAS_CTRL) | |
| printk(KERN_INFO " reg/bit %d/%d", p->reg, p->bit); | |
| printk("\n"); | |
| } | |
| mutex_unlock(&clocks_mutex); | |
| } | |
| #define DEBUG_CLK_DUMP() dump_clocks() | |
| #else | |
| #define DEBUG_CLK_DUMP() | |
| #endif | |
| static void mpc5121_clk_put(struct clk *clk) | |
| { | |
| module_put(clk->owner); | |
| } | |
| #define NRPSC 12 | |
| struct mpc512x_clockctl { | |
| u32 spmr; /* System PLL Mode Reg */ | |
| u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */ | |
| u32 scfr1; /* System Clk Freq Reg 1 */ | |
| u32 scfr2; /* System Clk Freq Reg 2 */ | |
| u32 reserved; | |
| u32 bcr; /* Bread Crumb Reg */ | |
| u32 pccr[NRPSC]; /* PSC Clk Ctrl Reg 0-11 */ | |
| u32 spccr; /* SPDIF Clk Ctrl Reg */ | |
| u32 cccr; /* CFM Clk Ctrl Reg */ | |
| u32 dccr; /* DIU Clk Cnfg Reg */ | |
| }; | |
| struct mpc512x_clockctl __iomem *clockctl; | |
| static int mpc5121_clk_enable(struct clk *clk) | |
| { | |
| unsigned int mask; | |
| if (clk->flags & CLK_HAS_CTRL) { | |
| mask = in_be32(&clockctl->sccr[clk->reg]); | |
| mask |= 1 << clk->bit; | |
| out_be32(&clockctl->sccr[clk->reg], mask); | |
| } | |
| return 0; | |
| } | |
| static void mpc5121_clk_disable(struct clk *clk) | |
| { | |
| unsigned int mask; | |
| if (clk->flags & CLK_HAS_CTRL) { | |
| mask = in_be32(&clockctl->sccr[clk->reg]); | |
| mask &= ~(1 << clk->bit); | |
| out_be32(&clockctl->sccr[clk->reg], mask); | |
| } | |
| } | |
| static unsigned long mpc5121_clk_get_rate(struct clk *clk) | |
| { | |
| if (clk->flags & CLK_HAS_RATE) | |
| return clk->rate; | |
| else | |
| return 0; | |
| } | |
| static long mpc5121_clk_round_rate(struct clk *clk, unsigned long rate) | |
| { | |
| return rate; | |
| } | |
| static int mpc5121_clk_set_rate(struct clk *clk, unsigned long rate) | |
| { | |
| return 0; | |
| } | |
| static int clk_register(struct clk *clk) | |
| { | |
| mutex_lock(&clocks_mutex); | |
| list_add(&clk->node, &clocks); | |
| mutex_unlock(&clocks_mutex); | |
| return 0; | |
| } | |
| static unsigned long spmf_mult(void) | |
| { | |
| /* | |
| * Convert spmf to multiplier | |
| */ | |
| static int spmf_to_mult[] = { | |
| 68, 1, 12, 16, | |
| 20, 24, 28, 32, | |
| 36, 40, 44, 48, | |
| 52, 56, 60, 64 | |
| }; | |
| int spmf = (clockctl->spmr >> 24) & 0xf; | |
| return spmf_to_mult[spmf]; | |
| } | |
| static unsigned long sysdiv_div_x_2(void) | |
| { | |
| /* | |
| * Convert sysdiv to divisor x 2 | |
| * Some divisors have fractional parts so | |
| * multiply by 2 then divide by this value | |
| */ | |
| static int sysdiv_to_div_x_2[] = { | |
| 4, 5, 6, 7, | |
| 8, 9, 10, 14, | |
| 12, 16, 18, 22, | |
| 20, 24, 26, 30, | |
| 28, 32, 34, 38, | |
| 36, 40, 42, 46, | |
| 44, 48, 50, 54, | |
| 52, 56, 58, 62, | |
| 60, 64, 66, | |
| }; | |
| int sysdiv = (clockctl->scfr2 >> 26) & 0x3f; | |
| return sysdiv_to_div_x_2[sysdiv]; | |
| } | |
| static unsigned long ref_to_sys(unsigned long rate) | |
| { | |
| rate *= spmf_mult(); | |
| rate *= 2; | |
| rate /= sysdiv_div_x_2(); | |
| return rate; | |
| } | |
| static unsigned long sys_to_ref(unsigned long rate) | |
| { | |
| rate *= sysdiv_div_x_2(); | |
| rate /= 2; | |
| rate /= spmf_mult(); | |
| return rate; | |
| } | |
| static long ips_to_ref(unsigned long rate) | |
| { | |
| int ips_div = (clockctl->scfr1 >> 23) & 0x7; | |
| rate *= ips_div; /* csb_clk = ips_clk * ips_div */ | |
| rate *= 2; /* sys_clk = csb_clk * 2 */ | |
| return sys_to_ref(rate); | |
| } | |
| static unsigned long devtree_getfreq(char *clockname) | |
| { | |
| struct device_node *np; | |
| const unsigned int *prop; | |
| unsigned int val = 0; | |
| np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-immr"); | |
| if (np) { | |
| prop = of_get_property(np, clockname, NULL); | |
| if (prop) | |
| val = *prop; | |
| of_node_put(np); | |
| } | |
| return val; | |
| } | |
| static void ref_clk_calc(struct clk *clk) | |
| { | |
| unsigned long rate; | |
| rate = devtree_getfreq("bus-frequency"); | |
| if (rate == 0) { | |
| printk(KERN_ERR "No bus-frequency in dev tree\n"); | |
| clk->rate = 0; | |
| return; | |
| } | |
| clk->rate = ips_to_ref(rate); | |
| } | |
| static struct clk ref_clk = { | |
| .name = "ref_clk", | |
| .calc = ref_clk_calc, | |
| }; | |
| static void sys_clk_calc(struct clk *clk) | |
| { | |
| clk->rate = ref_to_sys(ref_clk.rate); | |
| } | |
| static struct clk sys_clk = { | |
| .name = "sys_clk", | |
| .calc = sys_clk_calc, | |
| }; | |
| static void diu_clk_calc(struct clk *clk) | |
| { | |
| int diudiv_x_2 = clockctl->scfr1 & 0xff; | |
| unsigned long rate; | |
| rate = sys_clk.rate; | |
| rate *= 2; | |
| rate /= diudiv_x_2; | |
| clk->rate = rate; | |
| } | |
| static void half_clk_calc(struct clk *clk) | |
| { | |
| clk->rate = clk->parent->rate / 2; | |
| } | |
| static void generic_div_clk_calc(struct clk *clk) | |
| { | |
| int div = (clockctl->scfr1 >> clk->div_shift) & 0x7; | |
| clk->rate = clk->parent->rate / div; | |
| } | |
| static void unity_clk_calc(struct clk *clk) | |
| { | |
| clk->rate = clk->parent->rate; | |
| } | |
| static struct clk csb_clk = { | |
| .name = "csb_clk", | |
| .calc = half_clk_calc, | |
| .parent = &sys_clk, | |
| }; | |
| static void e300_clk_calc(struct clk *clk) | |
| { | |
| int spmf = (clockctl->spmr >> 16) & 0xf; | |
| int ratex2 = clk->parent->rate * spmf; | |
| clk->rate = ratex2 / 2; | |
| } | |
| static struct clk e300_clk = { | |
| .name = "e300_clk", | |
| .calc = e300_clk_calc, | |
| .parent = &csb_clk, | |
| }; | |
| static struct clk ips_clk = { | |
| .name = "ips_clk", | |
| .calc = generic_div_clk_calc, | |
| .parent = &csb_clk, | |
| .div_shift = 23, | |
| }; | |
| /* | |
| * Clocks controlled by SCCR1 (.reg = 0) | |
| */ | |
| static struct clk lpc_clk = { | |
| .name = "lpc_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 0, | |
| .bit = 30, | |
| .calc = generic_div_clk_calc, | |
| .parent = &ips_clk, | |
| .div_shift = 11, | |
| }; | |
| static struct clk nfc_clk = { | |
| .name = "nfc_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 0, | |
| .bit = 29, | |
| .calc = generic_div_clk_calc, | |
| .parent = &ips_clk, | |
| .div_shift = 8, | |
| }; | |
| static struct clk pata_clk = { | |
| .name = "pata_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 0, | |
| .bit = 28, | |
| .calc = unity_clk_calc, | |
| .parent = &ips_clk, | |
| }; | |
| /* | |
| * PSC clocks (bits 27 - 16) | |
| * are setup elsewhere | |
| */ | |
| static struct clk sata_clk = { | |
| .name = "sata_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 0, | |
| .bit = 14, | |
| .calc = unity_clk_calc, | |
| .parent = &ips_clk, | |
| }; | |
| static struct clk fec_clk = { | |
| .name = "fec_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 0, | |
| .bit = 13, | |
| .calc = unity_clk_calc, | |
| .parent = &ips_clk, | |
| }; | |
| static struct clk pci_clk = { | |
| .name = "pci_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 0, | |
| .bit = 11, | |
| .calc = generic_div_clk_calc, | |
| .parent = &csb_clk, | |
| .div_shift = 20, | |
| }; | |
| /* | |
| * Clocks controlled by SCCR2 (.reg = 1) | |
| */ | |
| static struct clk diu_clk = { | |
| .name = "diu_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 31, | |
| .calc = diu_clk_calc, | |
| }; | |
| static struct clk axe_clk = { | |
| .name = "axe_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 30, | |
| .calc = unity_clk_calc, | |
| .parent = &csb_clk, | |
| }; | |
| static struct clk usb1_clk = { | |
| .name = "usb1_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 28, | |
| .calc = unity_clk_calc, | |
| .parent = &csb_clk, | |
| }; | |
| static struct clk usb2_clk = { | |
| .name = "usb2_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 27, | |
| .calc = unity_clk_calc, | |
| .parent = &csb_clk, | |
| }; | |
| static struct clk i2c_clk = { | |
| .name = "i2c_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 26, | |
| .calc = unity_clk_calc, | |
| .parent = &ips_clk, | |
| }; | |
| static struct clk mscan_clk = { | |
| .name = "mscan_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 25, | |
| .calc = unity_clk_calc, | |
| .parent = &ips_clk, | |
| }; | |
| static struct clk sdhc_clk = { | |
| .name = "sdhc_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 24, | |
| .calc = unity_clk_calc, | |
| .parent = &ips_clk, | |
| }; | |
| static struct clk mbx_bus_clk = { | |
| .name = "mbx_bus_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 22, | |
| .calc = half_clk_calc, | |
| .parent = &csb_clk, | |
| }; | |
| static struct clk mbx_clk = { | |
| .name = "mbx_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 21, | |
| .calc = unity_clk_calc, | |
| .parent = &csb_clk, | |
| }; | |
| static struct clk mbx_3d_clk = { | |
| .name = "mbx_3d_clk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 20, | |
| .calc = generic_div_clk_calc, | |
| .parent = &mbx_bus_clk, | |
| .div_shift = 14, | |
| }; | |
| static void psc_mclk_in_calc(struct clk *clk) | |
| { | |
| clk->rate = devtree_getfreq("psc_mclk_in"); | |
| if (!clk->rate) | |
| clk->rate = 25000000; | |
| } | |
| static struct clk psc_mclk_in = { | |
| .name = "psc_mclk_in", | |
| .calc = psc_mclk_in_calc, | |
| }; | |
| static struct clk spdif_txclk = { | |
| .name = "spdif_txclk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 23, | |
| }; | |
| static struct clk spdif_rxclk = { | |
| .name = "spdif_rxclk", | |
| .flags = CLK_HAS_CTRL, | |
| .reg = 1, | |
| .bit = 23, | |
| }; | |
| static void ac97_clk_calc(struct clk *clk) | |
| { | |
| /* ac97 bit clock is always 24.567 MHz */ | |
| clk->rate = 24567000; | |
| } | |
| static struct clk ac97_clk = { | |
| .name = "ac97_clk_in", | |
| .calc = ac97_clk_calc, | |
| }; | |
| struct clk *rate_clks[] = { | |
| &ref_clk, | |
| &sys_clk, | |
| &diu_clk, | |
| &csb_clk, | |
| &e300_clk, | |
| &ips_clk, | |
| &fec_clk, | |
| &sata_clk, | |
| &pata_clk, | |
| &nfc_clk, | |
| &lpc_clk, | |
| &mbx_bus_clk, | |
| &mbx_clk, | |
| &mbx_3d_clk, | |
| &axe_clk, | |
| &usb1_clk, | |
| &usb2_clk, | |
| &i2c_clk, | |
| &mscan_clk, | |
| &sdhc_clk, | |
| &pci_clk, | |
| &psc_mclk_in, | |
| &spdif_txclk, | |
| &spdif_rxclk, | |
| &ac97_clk, | |
| NULL | |
| }; | |
| static void rate_clk_init(struct clk *clk) | |
| { | |
| if (clk->calc) { | |
| clk->calc(clk); | |
| clk->flags |= CLK_HAS_RATE; | |
| clk_register(clk); | |
| } else { | |
| printk(KERN_WARNING | |
| "Could not initialize clk %s without a calc routine\n", | |
| clk->name); | |
| } | |
| } | |
| static void rate_clks_init(void) | |
| { | |
| struct clk **cpp, *clk; | |
| cpp = rate_clks; | |
| while ((clk = *cpp++)) | |
| rate_clk_init(clk); | |
| } | |
| /* | |
| * There are two clk enable registers with 32 enable bits each | |
| * psc clocks and device clocks are all stored in dev_clks | |
| */ | |
| struct clk dev_clks[2][32]; | |
| /* | |
| * Given a psc number return the dev_clk | |
| * associated with it | |
| */ | |
| static struct clk *psc_dev_clk(int pscnum) | |
| { | |
| int reg, bit; | |
| struct clk *clk; | |
| reg = 0; | |
| bit = 27 - pscnum; | |
| clk = &dev_clks[reg][bit]; | |
| clk->reg = 0; | |
| clk->bit = bit; | |
| return clk; | |
| } | |
| /* | |
| * PSC clock rate calculation | |
| */ | |
| static void psc_calc_rate(struct clk *clk, int pscnum, struct device_node *np) | |
| { | |
| unsigned long mclk_src = sys_clk.rate; | |
| unsigned long mclk_div; | |
| /* | |
| * Can only change value of mclk divider | |
| * when the divider is disabled. | |
| * | |
| * Zero is not a valid divider so minimum | |
| * divider is 1 | |
| * | |
| * disable/set divider/enable | |
| */ | |
| out_be32(&clockctl->pccr[pscnum], 0); | |
| out_be32(&clockctl->pccr[pscnum], 0x00020000); | |
| out_be32(&clockctl->pccr[pscnum], 0x00030000); | |
| if (clockctl->pccr[pscnum] & 0x80) { | |
| clk->rate = spdif_rxclk.rate; | |
| return; | |
| } | |
| switch ((clockctl->pccr[pscnum] >> 14) & 0x3) { | |
| case 0: | |
| mclk_src = sys_clk.rate; | |
| break; | |
| case 1: | |
| mclk_src = ref_clk.rate; | |
| break; | |
| case 2: | |
| mclk_src = psc_mclk_in.rate; | |
| break; | |
| case 3: | |
| mclk_src = spdif_txclk.rate; | |
| break; | |
| } | |
| mclk_div = ((clockctl->pccr[pscnum] >> 17) & 0x7fff) + 1; | |
| clk->rate = mclk_src / mclk_div; | |
| } | |
| /* | |
| * Find all psc nodes in device tree and assign a clock | |
| * with name "psc%d_mclk" and dev pointing at the device | |
| * returned from of_find_device_by_node | |
| */ | |
| static void psc_clks_init(void) | |
| { | |
| struct device_node *np; | |
| const u32 *cell_index; | |
| struct of_device *ofdev; | |
| for_each_compatible_node(np, NULL, "fsl,mpc5121-psc") { | |
| cell_index = of_get_property(np, "cell-index", NULL); | |
| if (cell_index) { | |
| int pscnum = *cell_index; | |
| struct clk *clk = psc_dev_clk(pscnum); | |
| clk->flags = CLK_HAS_RATE | CLK_HAS_CTRL; | |
| ofdev = of_find_device_by_node(np); | |
| clk->dev = &ofdev->dev; | |
| /* | |
| * AC97 is special rate clock does | |
| * not go through normal path | |
| */ | |
| if (strcmp("ac97", np->name) == 0) | |
| clk->rate = ac97_clk.rate; | |
| else | |
| psc_calc_rate(clk, pscnum, np); | |
| sprintf(clk->name, "psc%d_mclk", pscnum); | |
| clk_register(clk); | |
| clk_enable(clk); | |
| } | |
| } | |
| } | |
| static struct clk_interface mpc5121_clk_functions = { | |
| .clk_get = mpc5121_clk_get, | |
| .clk_enable = mpc5121_clk_enable, | |
| .clk_disable = mpc5121_clk_disable, | |
| .clk_get_rate = mpc5121_clk_get_rate, | |
| .clk_put = mpc5121_clk_put, | |
| .clk_round_rate = mpc5121_clk_round_rate, | |
| .clk_set_rate = mpc5121_clk_set_rate, | |
| .clk_set_parent = NULL, | |
| .clk_get_parent = NULL, | |
| }; | |
| static int | |
| mpc5121_clk_init(void) | |
| { | |
| struct device_node *np; | |
| np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clock"); | |
| if (np) { | |
| clockctl = of_iomap(np, 0); | |
| of_node_put(np); | |
| } | |
| if (!clockctl) { | |
| printk(KERN_ERR "Could not map clock control registers\n"); | |
| return 0; | |
| } | |
| rate_clks_init(); | |
| psc_clks_init(); | |
| /* leave clockctl mapped forever */ | |
| /*iounmap(clockctl); */ | |
| DEBUG_CLK_DUMP(); | |
| clocks_initialized++; | |
| clk_functions = mpc5121_clk_functions; | |
| return 0; | |
| } | |
| arch_initcall(mpc5121_clk_init); |