123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 |
- /*
- * Nomadik clock implementation
- * Copyright (C) 2013 ST-Ericsson AB
- * License terms: GNU General Public License (GPL) version 2
- * Author: Linus Walleij <linus.walleij@linaro.org>
- */
- #define pr_fmt(fmt) "Nomadik SRC clocks: " fmt
- #include <linux/bitops.h>
- #include <linux/slab.h>
- #include <linux/err.h>
- #include <linux/io.h>
- #include <linux/clk-provider.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <linux/debugfs.h>
- #include <linux/seq_file.h>
- #include <linux/spinlock.h>
- #include <linux/reboot.h>
- /*
- * The Nomadik clock tree is described in the STN8815A12 DB V4.2
- * reference manual for the chip, page 94 ff.
- * Clock IDs are in the STn8815 Reference Manual table 3, page 27.
- */
- #define SRC_CR 0x00U
- #define SRC_CR_T0_ENSEL BIT(15)
- #define SRC_CR_T1_ENSEL BIT(17)
- #define SRC_CR_T2_ENSEL BIT(19)
- #define SRC_CR_T3_ENSEL BIT(21)
- #define SRC_CR_T4_ENSEL BIT(23)
- #define SRC_CR_T5_ENSEL BIT(25)
- #define SRC_CR_T6_ENSEL BIT(27)
- #define SRC_CR_T7_ENSEL BIT(29)
- #define SRC_XTALCR 0x0CU
- #define SRC_XTALCR_XTALTIMEN BIT(20)
- #define SRC_XTALCR_SXTALDIS BIT(19)
- #define SRC_XTALCR_MXTALSTAT BIT(2)
- #define SRC_XTALCR_MXTALEN BIT(1)
- #define SRC_XTALCR_MXTALOVER BIT(0)
- #define SRC_PLLCR 0x10U
- #define SRC_PLLCR_PLLTIMEN BIT(29)
- #define SRC_PLLCR_PLL2EN BIT(28)
- #define SRC_PLLCR_PLL1STAT BIT(2)
- #define SRC_PLLCR_PLL1EN BIT(1)
- #define SRC_PLLCR_PLL1OVER BIT(0)
- #define SRC_PLLFR 0x14U
- #define SRC_PCKEN0 0x24U
- #define SRC_PCKDIS0 0x28U
- #define SRC_PCKENSR0 0x2CU
- #define SRC_PCKSR0 0x30U
- #define SRC_PCKEN1 0x34U
- #define SRC_PCKDIS1 0x38U
- #define SRC_PCKENSR1 0x3CU
- #define SRC_PCKSR1 0x40U
- /* Lock protecting the SRC_CR register */
- static DEFINE_SPINLOCK(src_lock);
- /* Base address of the SRC */
- static void __iomem *src_base;
- static int nomadik_clk_reboot_handler(struct notifier_block *this,
- unsigned long code,
- void *unused)
- {
- u32 val;
- /* The main chrystal need to be enabled for reboot to work */
- val = readl(src_base + SRC_XTALCR);
- val &= ~SRC_XTALCR_MXTALOVER;
- val |= SRC_XTALCR_MXTALEN;
- pr_crit("force-enabling MXTALO\n");
- writel(val, src_base + SRC_XTALCR);
- return NOTIFY_OK;
- }
- static struct notifier_block nomadik_clk_reboot_notifier = {
- .notifier_call = nomadik_clk_reboot_handler,
- };
- static const struct of_device_id nomadik_src_match[] __initconst = {
- { .compatible = "stericsson,nomadik-src" },
- { /* sentinel */ }
- };
- static void __init nomadik_src_init(void)
- {
- struct device_node *np;
- u32 val;
- np = of_find_matching_node(NULL, nomadik_src_match);
- if (!np) {
- pr_crit("no matching node for SRC, aborting clock init\n");
- return;
- }
- src_base = of_iomap(np, 0);
- if (!src_base) {
- pr_err("%s: must have src parent node with REGS (%s)\n",
- __func__, np->name);
- return;
- }
- /* Set all timers to use the 2.4 MHz TIMCLK */
- val = readl(src_base + SRC_CR);
- val |= SRC_CR_T0_ENSEL;
- val |= SRC_CR_T1_ENSEL;
- val |= SRC_CR_T2_ENSEL;
- val |= SRC_CR_T3_ENSEL;
- val |= SRC_CR_T4_ENSEL;
- val |= SRC_CR_T5_ENSEL;
- val |= SRC_CR_T6_ENSEL;
- val |= SRC_CR_T7_ENSEL;
- writel(val, src_base + SRC_CR);
- val = readl(src_base + SRC_XTALCR);
- pr_info("SXTALO is %s\n",
- (val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled");
- pr_info("MXTAL is %s\n",
- (val & SRC_XTALCR_MXTALSTAT) ? "enabled" : "disabled");
- if (of_property_read_bool(np, "disable-sxtalo")) {
- /* The machine uses an external oscillator circuit */
- val |= SRC_XTALCR_SXTALDIS;
- pr_info("disabling SXTALO\n");
- }
- if (of_property_read_bool(np, "disable-mxtalo")) {
- /* Disable this too: also run by external oscillator */
- val |= SRC_XTALCR_MXTALOVER;
- val &= ~SRC_XTALCR_MXTALEN;
- pr_info("disabling MXTALO\n");
- }
- writel(val, src_base + SRC_XTALCR);
- register_reboot_notifier(&nomadik_clk_reboot_notifier);
- }
- /**
- * struct clk_pll1 - Nomadik PLL1 clock
- * @hw: corresponding clock hardware entry
- * @id: PLL instance: 1 or 2
- */
- struct clk_pll {
- struct clk_hw hw;
- int id;
- };
- /**
- * struct clk_src - Nomadik src clock
- * @hw: corresponding clock hardware entry
- * @id: the clock ID
- * @group1: true if the clock is in group1, else it is in group0
- * @clkbit: bit 0...31 corresponding to the clock in each clock register
- */
- struct clk_src {
- struct clk_hw hw;
- int id;
- bool group1;
- u32 clkbit;
- };
- #define to_pll(_hw) container_of(_hw, struct clk_pll, hw)
- #define to_src(_hw) container_of(_hw, struct clk_src, hw)
- static int pll_clk_enable(struct clk_hw *hw)
- {
- struct clk_pll *pll = to_pll(hw);
- u32 val;
- spin_lock(&src_lock);
- val = readl(src_base + SRC_PLLCR);
- if (pll->id == 1) {
- if (val & SRC_PLLCR_PLL1OVER) {
- val |= SRC_PLLCR_PLL1EN;
- writel(val, src_base + SRC_PLLCR);
- }
- } else if (pll->id == 2) {
- val |= SRC_PLLCR_PLL2EN;
- writel(val, src_base + SRC_PLLCR);
- }
- spin_unlock(&src_lock);
- return 0;
- }
- static void pll_clk_disable(struct clk_hw *hw)
- {
- struct clk_pll *pll = to_pll(hw);
- u32 val;
- spin_lock(&src_lock);
- val = readl(src_base + SRC_PLLCR);
- if (pll->id == 1) {
- if (val & SRC_PLLCR_PLL1OVER) {
- val &= ~SRC_PLLCR_PLL1EN;
- writel(val, src_base + SRC_PLLCR);
- }
- } else if (pll->id == 2) {
- val &= ~SRC_PLLCR_PLL2EN;
- writel(val, src_base + SRC_PLLCR);
- }
- spin_unlock(&src_lock);
- }
- static int pll_clk_is_enabled(struct clk_hw *hw)
- {
- struct clk_pll *pll = to_pll(hw);
- u32 val;
- val = readl(src_base + SRC_PLLCR);
- if (pll->id == 1) {
- if (val & SRC_PLLCR_PLL1OVER)
- return !!(val & SRC_PLLCR_PLL1EN);
- } else if (pll->id == 2) {
- return !!(val & SRC_PLLCR_PLL2EN);
- }
- return 1;
- }
- static unsigned long pll_clk_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- struct clk_pll *pll = to_pll(hw);
- u32 val;
- val = readl(src_base + SRC_PLLFR);
- if (pll->id == 1) {
- u8 mul;
- u8 div;
- mul = (val >> 8) & 0x3FU;
- mul += 2;
- div = val & 0x07U;
- return (parent_rate * mul) >> div;
- }
- if (pll->id == 2) {
- u8 mul;
- mul = (val >> 24) & 0x3FU;
- mul += 2;
- return (parent_rate * mul);
- }
- /* Unknown PLL */
- return 0;
- }
- static const struct clk_ops pll_clk_ops = {
- .enable = pll_clk_enable,
- .disable = pll_clk_disable,
- .is_enabled = pll_clk_is_enabled,
- .recalc_rate = pll_clk_recalc_rate,
- };
- static struct clk_hw * __init
- pll_clk_register(struct device *dev, const char *name,
- const char *parent_name, u32 id)
- {
- int ret;
- struct clk_pll *pll;
- struct clk_init_data init;
- if (id != 1 && id != 2) {
- pr_err("%s: the Nomadik has only PLL 1 & 2\n", __func__);
- return ERR_PTR(-EINVAL);
- }
- pll = kzalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll) {
- pr_err("%s: could not allocate PLL clk\n", __func__);
- return ERR_PTR(-ENOMEM);
- }
- init.name = name;
- init.ops = &pll_clk_ops;
- init.parent_names = (parent_name ? &parent_name : NULL);
- init.num_parents = (parent_name ? 1 : 0);
- pll->hw.init = &init;
- pll->id = id;
- pr_debug("register PLL1 clock \"%s\"\n", name);
- ret = clk_hw_register(dev, &pll->hw);
- if (ret) {
- kfree(pll);
- return ERR_PTR(ret);
- }
- return &pll->hw;
- }
- /*
- * The Nomadik SRC clocks are gated, but not in the sense that
- * you read-modify-write a register. Instead there are separate
- * clock enable and clock disable registers. Writing a '1' bit in
- * the enable register for a certain clock ungates that clock without
- * affecting the other clocks. The disable register works the opposite
- * way.
- */
- static int src_clk_enable(struct clk_hw *hw)
- {
- struct clk_src *sclk = to_src(hw);
- u32 enreg = sclk->group1 ? SRC_PCKEN1 : SRC_PCKEN0;
- u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0;
- writel(sclk->clkbit, src_base + enreg);
- /* spin until enabled */
- while (!(readl(src_base + sreg) & sclk->clkbit))
- cpu_relax();
- return 0;
- }
- static void src_clk_disable(struct clk_hw *hw)
- {
- struct clk_src *sclk = to_src(hw);
- u32 disreg = sclk->group1 ? SRC_PCKDIS1 : SRC_PCKDIS0;
- u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0;
- writel(sclk->clkbit, src_base + disreg);
- /* spin until disabled */
- while (readl(src_base + sreg) & sclk->clkbit)
- cpu_relax();
- }
- static int src_clk_is_enabled(struct clk_hw *hw)
- {
- struct clk_src *sclk = to_src(hw);
- u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0;
- u32 val = readl(src_base + sreg);
- return !!(val & sclk->clkbit);
- }
- static unsigned long
- src_clk_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- return parent_rate;
- }
- static const struct clk_ops src_clk_ops = {
- .enable = src_clk_enable,
- .disable = src_clk_disable,
- .is_enabled = src_clk_is_enabled,
- .recalc_rate = src_clk_recalc_rate,
- };
- static struct clk_hw * __init
- src_clk_register(struct device *dev, const char *name,
- const char *parent_name, u8 id)
- {
- int ret;
- struct clk_src *sclk;
- struct clk_init_data init;
- sclk = kzalloc(sizeof(*sclk), GFP_KERNEL);
- if (!sclk) {
- pr_err("could not allocate SRC clock %s\n",
- name);
- return ERR_PTR(-ENOMEM);
- }
- init.name = name;
- init.ops = &src_clk_ops;
- /* Do not force-disable the static SDRAM controller */
- if (id == 2)
- init.flags = CLK_IGNORE_UNUSED;
- else
- init.flags = 0;
- init.parent_names = (parent_name ? &parent_name : NULL);
- init.num_parents = (parent_name ? 1 : 0);
- sclk->hw.init = &init;
- sclk->id = id;
- sclk->group1 = (id > 31);
- sclk->clkbit = BIT(id & 0x1f);
- pr_debug("register clock \"%s\" ID: %d group: %d bits: %08x\n",
- name, id, sclk->group1, sclk->clkbit);
- ret = clk_hw_register(dev, &sclk->hw);
- if (ret) {
- kfree(sclk);
- return ERR_PTR(ret);
- }
- return &sclk->hw;
- }
- #ifdef CONFIG_DEBUG_FS
- static u32 src_pcksr0_boot;
- static u32 src_pcksr1_boot;
- static const char * const src_clk_names[] = {
- "HCLKDMA0 ",
- "HCLKSMC ",
- "HCLKSDRAM ",
- "HCLKDMA1 ",
- "HCLKCLCD ",
- "PCLKIRDA ",
- "PCLKSSP ",
- "PCLKUART0 ",
- "PCLKSDI ",
- "PCLKI2C0 ",
- "PCLKI2C1 ",
- "PCLKUART1 ",
- "PCLMSP0 ",
- "HCLKUSB ",
- "HCLKDIF ",
- "HCLKSAA ",
- "HCLKSVA ",
- "PCLKHSI ",
- "PCLKXTI ",
- "PCLKUART2 ",
- "PCLKMSP1 ",
- "PCLKMSP2 ",
- "PCLKOWM ",
- "HCLKHPI ",
- "PCLKSKE ",
- "PCLKHSEM ",
- "HCLK3D ",
- "HCLKHASH ",
- "HCLKCRYP ",
- "PCLKMSHC ",
- "HCLKUSBM ",
- "HCLKRNG ",
- "RESERVED ",
- "RESERVED ",
- "RESERVED ",
- "RESERVED ",
- "CLDCLK ",
- "IRDACLK ",
- "SSPICLK ",
- "UART0CLK ",
- "SDICLK ",
- "I2C0CLK ",
- "I2C1CLK ",
- "UART1CLK ",
- "MSPCLK0 ",
- "USBCLK ",
- "DIFCLK ",
- "IPI2CCLK ",
- "IPBMCCLK ",
- "HSICLKRX ",
- "HSICLKTX ",
- "UART2CLK ",
- "MSPCLK1 ",
- "MSPCLK2 ",
- "OWMCLK ",
- "RESERVED ",
- "SKECLK ",
- "RESERVED ",
- "3DCLK ",
- "PCLKMSP3 ",
- "MSPCLK3 ",
- "MSHCCLK ",
- "USBMCLK ",
- "RNGCCLK ",
- };
- static int nomadik_src_clk_show(struct seq_file *s, void *what)
- {
- int i;
- u32 src_pcksr0 = readl(src_base + SRC_PCKSR0);
- u32 src_pcksr1 = readl(src_base + SRC_PCKSR1);
- u32 src_pckensr0 = readl(src_base + SRC_PCKENSR0);
- u32 src_pckensr1 = readl(src_base + SRC_PCKENSR1);
- seq_printf(s, "Clock: Boot: Now: Request: ASKED:\n");
- for (i = 0; i < ARRAY_SIZE(src_clk_names); i++) {
- u32 pcksrb = (i < 0x20) ? src_pcksr0_boot : src_pcksr1_boot;
- u32 pcksr = (i < 0x20) ? src_pcksr0 : src_pcksr1;
- u32 pckreq = (i < 0x20) ? src_pckensr0 : src_pckensr1;
- u32 mask = BIT(i & 0x1f);
- seq_printf(s, "%s %s %s %s\n",
- src_clk_names[i],
- (pcksrb & mask) ? "on " : "off",
- (pcksr & mask) ? "on " : "off",
- (pckreq & mask) ? "on " : "off");
- }
- return 0;
- }
- static int nomadik_src_clk_open(struct inode *inode, struct file *file)
- {
- return single_open(file, nomadik_src_clk_show, NULL);
- }
- static const struct file_operations nomadik_src_clk_debugfs_ops = {
- .open = nomadik_src_clk_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int __init nomadik_src_clk_init_debugfs(void)
- {
- /* Vital for multiplatform */
- if (!src_base)
- return -ENODEV;
- src_pcksr0_boot = readl(src_base + SRC_PCKSR0);
- src_pcksr1_boot = readl(src_base + SRC_PCKSR1);
- debugfs_create_file("nomadik-src-clk", S_IFREG | S_IRUGO,
- NULL, NULL, &nomadik_src_clk_debugfs_ops);
- return 0;
- }
- device_initcall(nomadik_src_clk_init_debugfs);
- #endif
- static void __init of_nomadik_pll_setup(struct device_node *np)
- {
- struct clk_hw *hw;
- const char *clk_name = np->name;
- const char *parent_name;
- u32 pll_id;
- if (!src_base)
- nomadik_src_init();
- if (of_property_read_u32(np, "pll-id", &pll_id)) {
- pr_err("%s: PLL \"%s\" missing pll-id property\n",
- __func__, clk_name);
- return;
- }
- parent_name = of_clk_get_parent_name(np, 0);
- hw = pll_clk_register(NULL, clk_name, parent_name, pll_id);
- if (!IS_ERR(hw))
- of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
- }
- CLK_OF_DECLARE(nomadik_pll_clk,
- "st,nomadik-pll-clock", of_nomadik_pll_setup);
- static void __init of_nomadik_hclk_setup(struct device_node *np)
- {
- struct clk_hw *hw;
- const char *clk_name = np->name;
- const char *parent_name;
- if (!src_base)
- nomadik_src_init();
- parent_name = of_clk_get_parent_name(np, 0);
- /*
- * The HCLK divides PLL1 with 1 (passthru), 2, 3 or 4.
- */
- hw = clk_hw_register_divider(NULL, clk_name, parent_name,
- 0, src_base + SRC_CR,
- 13, 2,
- CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
- &src_lock);
- if (!IS_ERR(hw))
- of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
- }
- CLK_OF_DECLARE(nomadik_hclk_clk,
- "st,nomadik-hclk-clock", of_nomadik_hclk_setup);
- static void __init of_nomadik_src_clk_setup(struct device_node *np)
- {
- struct clk_hw *hw;
- const char *clk_name = np->name;
- const char *parent_name;
- u32 clk_id;
- if (!src_base)
- nomadik_src_init();
- if (of_property_read_u32(np, "clock-id", &clk_id)) {
- pr_err("%s: SRC clock \"%s\" missing clock-id property\n",
- __func__, clk_name);
- return;
- }
- parent_name = of_clk_get_parent_name(np, 0);
- hw = src_clk_register(NULL, clk_name, parent_name, clk_id);
- if (!IS_ERR(hw))
- of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
- }
- CLK_OF_DECLARE(nomadik_src_clk,
- "st,nomadik-src-clock", of_nomadik_src_clk_setup);
|