diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2015-09-01 09:35:24 -0400 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2015-09-01 09:35:24 -0400 |
| commit | 067e2601d3c076abbf45db91261f9065eaa879b2 (patch) | |
| tree | 86c8d4b913873dbd3b4ff23562a3a8597984b4df /drivers/clk/socfpga/clk-pll-a10.c | |
| parent | 3e097d1271ecdff2f251a54ddfc5eaa1f9821e96 (diff) | |
| parent | 931830aa5c251e0803523213428f777a48bde254 (diff) | |
Merge branch 'for-4.3/gembird' into for-linus
Diffstat (limited to 'drivers/clk/socfpga/clk-pll-a10.c')
| -rw-r--r-- | drivers/clk/socfpga/clk-pll-a10.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/drivers/clk/socfpga/clk-pll-a10.c b/drivers/clk/socfpga/clk-pll-a10.c new file mode 100644 index 000000000000..1178b11babca --- /dev/null +++ b/drivers/clk/socfpga/clk-pll-a10.c | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2015 Altera Corporation. All rights reserved | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | #include <linux/clk-provider.h> | ||
| 17 | #include <linux/io.h> | ||
| 18 | #include <linux/of.h> | ||
| 19 | #include <linux/of_address.h> | ||
| 20 | |||
| 21 | #include "clk.h" | ||
| 22 | |||
| 23 | /* Clock Manager offsets */ | ||
| 24 | #define CLK_MGR_PLL_CLK_SRC_SHIFT 8 | ||
| 25 | #define CLK_MGR_PLL_CLK_SRC_MASK 0x3 | ||
| 26 | |||
| 27 | /* Clock bypass bits */ | ||
| 28 | #define SOCFPGA_PLL_BG_PWRDWN 0 | ||
| 29 | #define SOCFPGA_PLL_PWR_DOWN 1 | ||
| 30 | #define SOCFPGA_PLL_EXT_ENA 2 | ||
| 31 | #define SOCFPGA_PLL_DIVF_MASK 0x00001FFF | ||
| 32 | #define SOCFPGA_PLL_DIVF_SHIFT 0 | ||
| 33 | #define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 | ||
| 34 | #define SOCFPGA_PLL_DIVQ_SHIFT 16 | ||
| 35 | #define SOCFGPA_MAX_PARENTS 5 | ||
| 36 | |||
| 37 | #define SOCFPGA_MAIN_PLL_CLK "main_pll" | ||
| 38 | #define SOCFPGA_PERIP_PLL_CLK "periph_pll" | ||
| 39 | |||
| 40 | #define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) | ||
| 41 | |||
| 42 | void __iomem *clk_mgr_a10_base_addr; | ||
| 43 | |||
| 44 | static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, | ||
| 45 | unsigned long parent_rate) | ||
| 46 | { | ||
| 47 | struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); | ||
| 48 | unsigned long divf, divq, reg; | ||
| 49 | unsigned long long vco_freq; | ||
| 50 | |||
| 51 | /* read VCO1 reg for numerator and denominator */ | ||
| 52 | reg = readl(socfpgaclk->hw.reg + 0x4); | ||
| 53 | divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; | ||
| 54 | divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; | ||
| 55 | vco_freq = (unsigned long long)parent_rate * (divf + 1); | ||
| 56 | do_div(vco_freq, (1 + divq)); | ||
| 57 | return (unsigned long)vco_freq; | ||
| 58 | } | ||
| 59 | |||
| 60 | static u8 clk_pll_get_parent(struct clk_hw *hwclk) | ||
| 61 | { | ||
| 62 | struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); | ||
| 63 | u32 pll_src; | ||
| 64 | |||
| 65 | pll_src = readl(socfpgaclk->hw.reg); | ||
| 66 | |||
| 67 | return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) & | ||
| 68 | CLK_MGR_PLL_CLK_SRC_MASK; | ||
| 69 | } | ||
| 70 | |||
| 71 | static struct clk_ops clk_pll_ops = { | ||
| 72 | .recalc_rate = clk_pll_recalc_rate, | ||
| 73 | .get_parent = clk_pll_get_parent, | ||
| 74 | }; | ||
| 75 | |||
| 76 | static struct __init clk * __socfpga_pll_init(struct device_node *node, | ||
| 77 | const struct clk_ops *ops) | ||
| 78 | { | ||
| 79 | u32 reg; | ||
| 80 | struct clk *clk; | ||
| 81 | struct socfpga_pll *pll_clk; | ||
| 82 | const char *clk_name = node->name; | ||
| 83 | const char *parent_name[SOCFGPA_MAX_PARENTS]; | ||
| 84 | struct clk_init_data init; | ||
| 85 | struct device_node *clkmgr_np; | ||
| 86 | int rc; | ||
| 87 | int i = 0; | ||
| 88 | |||
| 89 | of_property_read_u32(node, "reg", ®); | ||
| 90 | |||
| 91 | pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); | ||
| 92 | if (WARN_ON(!pll_clk)) | ||
| 93 | return NULL; | ||
| 94 | |||
| 95 | clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); | ||
| 96 | clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0); | ||
| 97 | BUG_ON(!clk_mgr_a10_base_addr); | ||
| 98 | pll_clk->hw.reg = clk_mgr_a10_base_addr + reg; | ||
| 99 | |||
| 100 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
| 101 | |||
| 102 | init.name = clk_name; | ||
| 103 | init.ops = ops; | ||
| 104 | init.flags = 0; | ||
| 105 | |||
| 106 | while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] = | ||
| 107 | of_clk_get_parent_name(node, i)) != NULL) | ||
| 108 | i++; | ||
| 109 | init.num_parents = i; | ||
| 110 | init.parent_names = parent_name; | ||
| 111 | pll_clk->hw.hw.init = &init; | ||
| 112 | |||
| 113 | pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; | ||
| 114 | clk_pll_ops.enable = clk_gate_ops.enable; | ||
| 115 | clk_pll_ops.disable = clk_gate_ops.disable; | ||
| 116 | |||
| 117 | clk = clk_register(NULL, &pll_clk->hw.hw); | ||
| 118 | if (WARN_ON(IS_ERR(clk))) { | ||
| 119 | kfree(pll_clk); | ||
| 120 | return NULL; | ||
| 121 | } | ||
| 122 | rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
| 123 | return clk; | ||
| 124 | } | ||
| 125 | |||
| 126 | void __init socfpga_a10_pll_init(struct device_node *node) | ||
| 127 | { | ||
| 128 | __socfpga_pll_init(node, &clk_pll_ops); | ||
| 129 | } | ||
