Commit | Line | Data |
---|---|---|
8f8f484b PG |
1 | /* |
2 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * 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 | |
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/delay.h> | |
20 | #include <linux/err.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/clk-provider.h> | |
8f8f484b PG |
23 | |
24 | #include "clk.h" | |
25 | ||
26 | #define SUPER_STATE_IDLE 0 | |
27 | #define SUPER_STATE_RUN 1 | |
28 | #define SUPER_STATE_IRQ 2 | |
29 | #define SUPER_STATE_FIQ 3 | |
30 | ||
31 | #define SUPER_STATE_SHIFT 28 | |
32 | #define SUPER_STATE_MASK ((BIT(SUPER_STATE_IDLE) | BIT(SUPER_STATE_RUN) | \ | |
33 | BIT(SUPER_STATE_IRQ) | BIT(SUPER_STATE_FIQ)) \ | |
34 | << SUPER_STATE_SHIFT) | |
35 | ||
36 | #define SUPER_LP_DIV2_BYPASS (1 << 16) | |
37 | ||
38 | #define super_state(s) (BIT(s) << SUPER_STATE_SHIFT) | |
39 | #define super_state_to_src_shift(m, s) ((m->width * s)) | |
40 | #define super_state_to_src_mask(m) (((1 << m->width) - 1)) | |
41 | ||
42 | static u8 clk_super_get_parent(struct clk_hw *hw) | |
43 | { | |
44 | struct tegra_clk_super_mux *mux = to_clk_super_mux(hw); | |
45 | u32 val, state; | |
46 | u8 source, shift; | |
47 | ||
48 | val = readl_relaxed(mux->reg); | |
49 | ||
50 | state = val & SUPER_STATE_MASK; | |
51 | ||
52 | BUG_ON((state != super_state(SUPER_STATE_RUN)) && | |
53 | (state != super_state(SUPER_STATE_IDLE))); | |
54 | shift = (state == super_state(SUPER_STATE_IDLE)) ? | |
55 | super_state_to_src_shift(mux, SUPER_STATE_IDLE) : | |
56 | super_state_to_src_shift(mux, SUPER_STATE_RUN); | |
57 | ||
58 | source = (val >> shift) & super_state_to_src_mask(mux); | |
59 | ||
60 | /* | |
61 | * If LP_DIV2_BYPASS is not set and PLLX is current parent then | |
62 | * PLLX/2 is the input source to CCLKLP. | |
63 | */ | |
64 | if ((mux->flags & TEGRA_DIVIDER_2) && !(val & SUPER_LP_DIV2_BYPASS) && | |
65 | (source == mux->pllx_index)) | |
66 | source = mux->div2_index; | |
67 | ||
68 | return source; | |
69 | } | |
70 | ||
71 | static int clk_super_set_parent(struct clk_hw *hw, u8 index) | |
72 | { | |
73 | struct tegra_clk_super_mux *mux = to_clk_super_mux(hw); | |
74 | u32 val, state; | |
c64c65d4 | 75 | int err = 0; |
8f8f484b | 76 | u8 parent_index, shift; |
c64c65d4 PDS |
77 | unsigned long flags = 0; |
78 | ||
79 | if (mux->lock) | |
80 | spin_lock_irqsave(mux->lock, flags); | |
8f8f484b PG |
81 | |
82 | val = readl_relaxed(mux->reg); | |
83 | state = val & SUPER_STATE_MASK; | |
84 | BUG_ON((state != super_state(SUPER_STATE_RUN)) && | |
85 | (state != super_state(SUPER_STATE_IDLE))); | |
86 | shift = (state == super_state(SUPER_STATE_IDLE)) ? | |
87 | super_state_to_src_shift(mux, SUPER_STATE_IDLE) : | |
88 | super_state_to_src_shift(mux, SUPER_STATE_RUN); | |
89 | ||
90 | /* | |
91 | * For LP mode super-clock switch between PLLX direct | |
92 | * and divided-by-2 outputs is allowed only when other | |
93 | * than PLLX clock source is current parent. | |
94 | */ | |
95 | if ((mux->flags & TEGRA_DIVIDER_2) && ((index == mux->div2_index) || | |
96 | (index == mux->pllx_index))) { | |
97 | parent_index = clk_super_get_parent(hw); | |
98 | if ((parent_index == mux->div2_index) || | |
c64c65d4 PDS |
99 | (parent_index == mux->pllx_index)) { |
100 | err = -EINVAL; | |
101 | goto out; | |
102 | } | |
8f8f484b PG |
103 | |
104 | val ^= SUPER_LP_DIV2_BYPASS; | |
105 | writel_relaxed(val, mux->reg); | |
106 | udelay(2); | |
107 | ||
108 | if (index == mux->div2_index) | |
109 | index = mux->pllx_index; | |
110 | } | |
111 | val &= ~((super_state_to_src_mask(mux)) << shift); | |
112 | val |= (index & (super_state_to_src_mask(mux))) << shift; | |
113 | ||
114 | writel_relaxed(val, mux->reg); | |
115 | udelay(2); | |
c64c65d4 PDS |
116 | |
117 | out: | |
118 | if (mux->lock) | |
119 | spin_unlock_irqrestore(mux->lock, flags); | |
120 | ||
121 | return err; | |
8f8f484b PG |
122 | } |
123 | ||
124 | const struct clk_ops tegra_clk_super_ops = { | |
125 | .get_parent = clk_super_get_parent, | |
126 | .set_parent = clk_super_set_parent, | |
127 | }; | |
128 | ||
129 | struct clk *tegra_clk_register_super_mux(const char *name, | |
130 | const char **parent_names, u8 num_parents, | |
131 | unsigned long flags, void __iomem *reg, u8 clk_super_flags, | |
132 | u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock) | |
133 | { | |
134 | struct tegra_clk_super_mux *super; | |
135 | struct clk *clk; | |
136 | struct clk_init_data init; | |
137 | ||
138 | super = kzalloc(sizeof(*super), GFP_KERNEL); | |
139 | if (!super) { | |
140 | pr_err("%s: could not allocate super clk\n", __func__); | |
141 | return ERR_PTR(-ENOMEM); | |
142 | } | |
143 | ||
144 | init.name = name; | |
145 | init.ops = &tegra_clk_super_ops; | |
146 | init.flags = flags; | |
147 | init.parent_names = parent_names; | |
148 | init.num_parents = num_parents; | |
149 | ||
150 | super->reg = reg; | |
151 | super->pllx_index = pllx_index; | |
152 | super->div2_index = div2_index; | |
153 | super->lock = lock; | |
154 | super->width = width; | |
155 | super->flags = clk_super_flags; | |
156 | ||
157 | /* Data in .init is copied by clk_register(), so stack variable OK */ | |
158 | super->hw.init = &init; | |
159 | ||
160 | clk = clk_register(NULL, &super->hw); | |
161 | if (IS_ERR(clk)) | |
162 | kfree(super); | |
163 | ||
164 | return clk; | |
165 | } |