| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * 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 | 4 | */ |
|---|
| 16 | 5 | |
|---|
| 17 | 6 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 38 | 27 | #define super_state(s) (BIT(s) << SUPER_STATE_SHIFT) |
|---|
| 39 | 28 | #define super_state_to_src_shift(m, s) ((m->width * s)) |
|---|
| 40 | 29 | #define super_state_to_src_mask(m) (((1 << m->width) - 1)) |
|---|
| 30 | + |
|---|
| 31 | +#define CCLK_SRC_PLLP_OUT0 4 |
|---|
| 32 | +#define CCLK_SRC_PLLP_OUT4 5 |
|---|
| 41 | 33 | |
|---|
| 42 | 34 | static u8 clk_super_get_parent(struct clk_hw *hw) |
|---|
| 43 | 35 | { |
|---|
| .. | .. |
|---|
| 108 | 100 | if (index == mux->div2_index) |
|---|
| 109 | 101 | index = mux->pllx_index; |
|---|
| 110 | 102 | } |
|---|
| 103 | + |
|---|
| 104 | + /* enable PLLP branches to CPU before selecting PLLP source */ |
|---|
| 105 | + if ((mux->flags & TEGRA210_CPU_CLK) && |
|---|
| 106 | + (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4)) |
|---|
| 107 | + tegra_clk_set_pllp_out_cpu(true); |
|---|
| 108 | + |
|---|
| 111 | 109 | val &= ~((super_state_to_src_mask(mux)) << shift); |
|---|
| 112 | 110 | val |= (index & (super_state_to_src_mask(mux))) << shift; |
|---|
| 113 | 111 | |
|---|
| 114 | 112 | writel_relaxed(val, mux->reg); |
|---|
| 115 | 113 | udelay(2); |
|---|
| 114 | + |
|---|
| 115 | + /* disable PLLP branches to CPU if not used */ |
|---|
| 116 | + if ((mux->flags & TEGRA210_CPU_CLK) && |
|---|
| 117 | + index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4) |
|---|
| 118 | + tegra_clk_set_pllp_out_cpu(false); |
|---|
| 116 | 119 | |
|---|
| 117 | 120 | out: |
|---|
| 118 | 121 | if (mux->lock) |
|---|
| .. | .. |
|---|
| 121 | 124 | return err; |
|---|
| 122 | 125 | } |
|---|
| 123 | 126 | |
|---|
| 124 | | -const struct clk_ops tegra_clk_super_mux_ops = { |
|---|
| 127 | +static void clk_super_mux_restore_context(struct clk_hw *hw) |
|---|
| 128 | +{ |
|---|
| 129 | + int parent_id; |
|---|
| 130 | + |
|---|
| 131 | + parent_id = clk_hw_get_parent_index(hw); |
|---|
| 132 | + if (WARN_ON(parent_id < 0)) |
|---|
| 133 | + return; |
|---|
| 134 | + |
|---|
| 135 | + clk_super_set_parent(hw, parent_id); |
|---|
| 136 | +} |
|---|
| 137 | + |
|---|
| 138 | +static const struct clk_ops tegra_clk_super_mux_ops = { |
|---|
| 125 | 139 | .get_parent = clk_super_get_parent, |
|---|
| 126 | 140 | .set_parent = clk_super_set_parent, |
|---|
| 141 | + .restore_context = clk_super_mux_restore_context, |
|---|
| 127 | 142 | }; |
|---|
| 128 | 143 | |
|---|
| 129 | 144 | static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate, |
|---|
| .. | .. |
|---|
| 159 | 174 | return super->div_ops->set_rate(div_hw, rate, parent_rate); |
|---|
| 160 | 175 | } |
|---|
| 161 | 176 | |
|---|
| 177 | +static void clk_super_restore_context(struct clk_hw *hw) |
|---|
| 178 | +{ |
|---|
| 179 | + struct tegra_clk_super_mux *super = to_clk_super_mux(hw); |
|---|
| 180 | + struct clk_hw *div_hw = &super->frac_div.hw; |
|---|
| 181 | + int parent_id; |
|---|
| 182 | + |
|---|
| 183 | + parent_id = clk_hw_get_parent_index(hw); |
|---|
| 184 | + if (WARN_ON(parent_id < 0)) |
|---|
| 185 | + return; |
|---|
| 186 | + |
|---|
| 187 | + super->div_ops->restore_context(div_hw); |
|---|
| 188 | + clk_super_set_parent(hw, parent_id); |
|---|
| 189 | +} |
|---|
| 190 | + |
|---|
| 162 | 191 | const struct clk_ops tegra_clk_super_ops = { |
|---|
| 163 | 192 | .get_parent = clk_super_get_parent, |
|---|
| 164 | 193 | .set_parent = clk_super_set_parent, |
|---|
| 165 | 194 | .set_rate = clk_super_set_rate, |
|---|
| 166 | 195 | .round_rate = clk_super_round_rate, |
|---|
| 167 | 196 | .recalc_rate = clk_super_recalc_rate, |
|---|
| 197 | + .restore_context = clk_super_restore_context, |
|---|
| 168 | 198 | }; |
|---|
| 169 | 199 | |
|---|
| 170 | 200 | struct clk *tegra_clk_register_super_mux(const char *name, |
|---|
| .. | .. |
|---|
| 174 | 204 | { |
|---|
| 175 | 205 | struct tegra_clk_super_mux *super; |
|---|
| 176 | 206 | struct clk *clk; |
|---|
| 177 | | - struct clk_init_data init = {}; |
|---|
| 207 | + struct clk_init_data init; |
|---|
| 178 | 208 | |
|---|
| 179 | 209 | super = kzalloc(sizeof(*super), GFP_KERNEL); |
|---|
| 180 | 210 | if (!super) |
|---|
| .. | .. |
|---|
| 210 | 240 | { |
|---|
| 211 | 241 | struct tegra_clk_super_mux *super; |
|---|
| 212 | 242 | struct clk *clk; |
|---|
| 213 | | - struct clk_init_data init = {}; |
|---|
| 243 | + struct clk_init_data init; |
|---|
| 214 | 244 | |
|---|
| 215 | 245 | super = kzalloc(sizeof(*super), GFP_KERNEL); |
|---|
| 216 | 246 | if (!super) |
|---|