123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- /*
- * SH7372 clock framework support
- *
- * Copyright (C) 2010 Magnus Damm
- *
- * 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
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/io.h>
- #include <linux/sh_clk.h>
- #include <linux/clkdev.h>
- #include <mach/common.h>
- /* SH7372 registers */
- #define FRQCRA IOMEM(0xe6150000)
- #define FRQCRB IOMEM(0xe6150004)
- #define FRQCRC IOMEM(0xe61500e0)
- #define FRQCRD IOMEM(0xe61500e4)
- #define VCLKCR1 IOMEM(0xe6150008)
- #define VCLKCR2 IOMEM(0xe615000c)
- #define VCLKCR3 IOMEM(0xe615001c)
- #define FMSICKCR IOMEM(0xe6150010)
- #define FMSOCKCR IOMEM(0xe6150014)
- #define FSIACKCR IOMEM(0xe6150018)
- #define FSIBCKCR IOMEM(0xe6150090)
- #define SUBCKCR IOMEM(0xe6150080)
- #define SPUCKCR IOMEM(0xe6150084)
- #define VOUCKCR IOMEM(0xe6150088)
- #define HDMICKCR IOMEM(0xe6150094)
- #define DSITCKCR IOMEM(0xe6150060)
- #define DSI0PCKCR IOMEM(0xe6150064)
- #define DSI1PCKCR IOMEM(0xe6150098)
- #define PLLC01CR IOMEM(0xe6150028)
- #define PLLC2CR IOMEM(0xe615002c)
- #define RMSTPCR0 IOMEM(0xe6150110)
- #define RMSTPCR1 IOMEM(0xe6150114)
- #define RMSTPCR2 IOMEM(0xe6150118)
- #define RMSTPCR3 IOMEM(0xe615011c)
- #define RMSTPCR4 IOMEM(0xe6150120)
- #define SMSTPCR0 IOMEM(0xe6150130)
- #define SMSTPCR1 IOMEM(0xe6150134)
- #define SMSTPCR2 IOMEM(0xe6150138)
- #define SMSTPCR3 IOMEM(0xe615013c)
- #define SMSTPCR4 IOMEM(0xe6150140)
- #define FSIDIVA 0xFE1F8000
- #define FSIDIVB 0xFE1F8008
- /* Platforms must set frequency on their DV_CLKI pin */
- struct clk sh7372_dv_clki_clk = {
- };
- /* Fixed 32 KHz root clock from EXTALR pin */
- static struct clk r_clk = {
- .rate = 32768,
- };
- /*
- * 26MHz default rate for the EXTAL1 root input clock.
- * If needed, reset this with clk_set_rate() from the platform code.
- */
- struct clk sh7372_extal1_clk = {
- .rate = 26000000,
- };
- /*
- * 48MHz default rate for the EXTAL2 root input clock.
- * If needed, reset this with clk_set_rate() from the platform code.
- */
- struct clk sh7372_extal2_clk = {
- .rate = 48000000,
- };
- /* A fixed divide-by-2 block */
- static unsigned long div2_recalc(struct clk *clk)
- {
- return clk->parent->rate / 2;
- }
- static struct sh_clk_ops div2_clk_ops = {
- .recalc = div2_recalc,
- };
- /* Divide dv_clki by two */
- struct clk sh7372_dv_clki_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &sh7372_dv_clki_clk,
- };
- /* Divide extal1 by two */
- static struct clk extal1_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &sh7372_extal1_clk,
- };
- /* Divide extal2 by two */
- static struct clk extal2_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &sh7372_extal2_clk,
- };
- /* Divide extal2 by four */
- static struct clk extal2_div4_clk = {
- .ops = &div2_clk_ops,
- .parent = &extal2_div2_clk,
- };
- /* PLLC0 and PLLC1 */
- static unsigned long pllc01_recalc(struct clk *clk)
- {
- unsigned long mult = 1;
- if (__raw_readl(PLLC01CR) & (1 << 14))
- mult = (((__raw_readl(clk->enable_reg) >> 24) & 0x3f) + 1) * 2;
- return clk->parent->rate * mult;
- }
- static struct sh_clk_ops pllc01_clk_ops = {
- .recalc = pllc01_recalc,
- };
- static struct clk pllc0_clk = {
- .ops = &pllc01_clk_ops,
- .flags = CLK_ENABLE_ON_INIT,
- .parent = &extal1_div2_clk,
- .enable_reg = (void __iomem *)FRQCRC,
- };
- static struct clk pllc1_clk = {
- .ops = &pllc01_clk_ops,
- .flags = CLK_ENABLE_ON_INIT,
- .parent = &extal1_div2_clk,
- .enable_reg = (void __iomem *)FRQCRA,
- };
- /* Divide PLLC1 by two */
- static struct clk pllc1_div2_clk = {
- .ops = &div2_clk_ops,
- .parent = &pllc1_clk,
- };
- /* PLLC2 */
- /* Indices are important - they are the actual src selecting values */
- static struct clk *pllc2_parent[] = {
- [0] = &extal1_div2_clk,
- [1] = &extal2_div2_clk,
- [2] = &sh7372_dv_clki_div2_clk,
- };
- /* Only multipliers 20 * 2 to 46 * 2 are valid, last entry for CPUFREQ_TABLE_END */
- static struct cpufreq_frequency_table pllc2_freq_table[29];
- static void pllc2_table_rebuild(struct clk *clk)
- {
- int i;
- /* Initialise PLLC2 frequency table */
- for (i = 0; i < ARRAY_SIZE(pllc2_freq_table) - 2; i++) {
- pllc2_freq_table[i].frequency = clk->parent->rate * (i + 20) * 2;
- pllc2_freq_table[i].index = i;
- }
- /* This is a special entry - switching PLL off makes it a repeater */
- pllc2_freq_table[i].frequency = clk->parent->rate;
- pllc2_freq_table[i].index = i;
- pllc2_freq_table[++i].frequency = CPUFREQ_TABLE_END;
- pllc2_freq_table[i].index = i;
- }
- static unsigned long pllc2_recalc(struct clk *clk)
- {
- unsigned long mult = 1;
- pllc2_table_rebuild(clk);
- /*
- * If the PLL is off, mult == 1, clk->rate will be updated in
- * pllc2_enable().
- */
- if (__raw_readl(PLLC2CR) & (1 << 31))
- mult = (((__raw_readl(PLLC2CR) >> 24) & 0x3f) + 1) * 2;
- return clk->parent->rate * mult;
- }
- static long pllc2_round_rate(struct clk *clk, unsigned long rate)
- {
- return clk_rate_table_round(clk, clk->freq_table, rate);
- }
- static int pllc2_enable(struct clk *clk)
- {
- int i;
- __raw_writel(__raw_readl(PLLC2CR) | 0x80000000, PLLC2CR);
- for (i = 0; i < 100; i++)
- if (__raw_readl(PLLC2CR) & 0x80000000) {
- clk->rate = pllc2_recalc(clk);
- return 0;
- }
- pr_err("%s(): timeout!\n", __func__);
- return -ETIMEDOUT;
- }
- static void pllc2_disable(struct clk *clk)
- {
- __raw_writel(__raw_readl(PLLC2CR) & ~0x80000000, PLLC2CR);
- }
- static int pllc2_set_rate(struct clk *clk, unsigned long rate)
- {
- unsigned long value;
- int idx;
- idx = clk_rate_table_find(clk, clk->freq_table, rate);
- if (idx < 0)
- return idx;
- if (rate == clk->parent->rate)
- return -EINVAL;
- value = __raw_readl(PLLC2CR) & ~(0x3f << 24);
- __raw_writel(value | ((idx + 19) << 24), PLLC2CR);
- clk->rate = clk->freq_table[idx].frequency;
- return 0;
- }
- static int pllc2_set_parent(struct clk *clk, struct clk *parent)
- {
- u32 value;
- int ret, i;
- if (!clk->parent_table || !clk->parent_num)
- return -EINVAL;
- /* Search the parent */
- for (i = 0; i < clk->parent_num; i++)
- if (clk->parent_table[i] == parent)
- break;
- if (i == clk->parent_num)
- return -ENODEV;
- ret = clk_reparent(clk, parent);
- if (ret < 0)
- return ret;
- value = __raw_readl(PLLC2CR) & ~(3 << 6);
- __raw_writel(value | (i << 6), PLLC2CR);
- /* Rebiuld the frequency table */
- pllc2_table_rebuild(clk);
- return 0;
- }
- static struct sh_clk_ops pllc2_clk_ops = {
- .recalc = pllc2_recalc,
- .round_rate = pllc2_round_rate,
- .set_rate = pllc2_set_rate,
- .enable = pllc2_enable,
- .disable = pllc2_disable,
- .set_parent = pllc2_set_parent,
- };
- struct clk sh7372_pllc2_clk = {
- .ops = &pllc2_clk_ops,
- .parent = &extal1_div2_clk,
- .freq_table = pllc2_freq_table,
- .nr_freqs = ARRAY_SIZE(pllc2_freq_table) - 1,
- .parent_table = pllc2_parent,
- .parent_num = ARRAY_SIZE(pllc2_parent),
- };
- /* External input clock (pin name: FSIACK/FSIBCK ) */
- static struct clk fsiack_clk = {
- };
- static struct clk fsibck_clk = {
- };
- static struct clk *main_clks[] = {
- &sh7372_dv_clki_clk,
- &r_clk,
- &sh7372_extal1_clk,
- &sh7372_extal2_clk,
- &sh7372_dv_clki_div2_clk,
- &extal1_div2_clk,
- &extal2_div2_clk,
- &extal2_div4_clk,
- &pllc0_clk,
- &pllc1_clk,
- &pllc1_div2_clk,
- &sh7372_pllc2_clk,
- &fsiack_clk,
- &fsibck_clk,
- };
- static void div4_kick(struct clk *clk)
- {
- unsigned long value;
- /* set KICK bit in FRQCRB to update hardware setting */
- value = __raw_readl(FRQCRB);
- value |= (1 << 31);
- __raw_writel(value, FRQCRB);
- }
|