#include "pwrcal-env.h" #include "pwrcal-vclk.h" #include "pwrcal-pmu.h" #include "pwrcal-clk.h" #include "pwrcal-rae.h" #include #include #define is_vclk(id) ((id & 0x0F000000) == 0x0A000000) unsigned int _cal_vclk_get(char *name) { unsigned int id; for (id = 0; id < vclk_grpgate_list_size; id++) if (!strcmp(vclk_grpgate_list[id]->vclk.name, name)) return vclk_group_grpgate + id; for (id = 0; id < vclk_m1d1g1_list_size; id++) if (!strcmp(vclk_m1d1g1_list[id]->vclk.name, name)) return vclk_group_m1d1g1 + id; for (id = 0; id < vclk_p1_list_size; id++) if (!strcmp(vclk_p1_list[id]->vclk.name, name)) return vclk_group_p1 + id; for (id = 0; id < vclk_m1_list_size; id++) if (!strcmp(vclk_m1_list[id]->vclk.name, name)) return vclk_group_m1 + id; for (id = 0; id < vclk_d1_list_size; id++) if (!strcmp(vclk_d1_list[id]->vclk.name, name)) return vclk_group_d1 + id; for (id = 0; id < vclk_pxmxdx_list_size; id++) if (!strcmp(vclk_pxmxdx_list[id]->vclk.name, name)) return vclk_group_pxmxdx + id; for (id = 0; id < vclk_umux_list_size; id++) if (!strcmp(vclk_umux_list[id]->vclk.name, name)) return vclk_group_umux + id; for (id = 0; id < vclk_dfs_list_size; id++) if (!strcmp(vclk_dfs_list[id]->vclk.name, name)) return vclk_group_dfs + id; return 0; } struct vclk *cal_get_vclk(unsigned int id) { struct vclk *ret = NULL; if (!is_vclk(id)) return ret; switch (id & vclk_group_mask) { case vclk_group_grpgate: ret = &(vclk_grpgate_list[id & 0x0000FFFF]->vclk); break; case vclk_group_m1d1g1: ret = &(vclk_m1d1g1_list[id & 0x0000FFFF]->vclk); break; case vclk_group_p1: ret = &(vclk_p1_list[id & 0x0000FFFF]->vclk); break; case vclk_group_m1: ret = &(vclk_m1_list[id & 0x0000FFFF]->vclk); break; case vclk_group_d1: ret = &(vclk_d1_list[id & 0x0000FFFF]->vclk); break; case vclk_group_pxmxdx: ret = &(vclk_pxmxdx_list[id & 0x0000FFFF]->vclk); break; case vclk_group_umux: ret = &(vclk_umux_list[id & 0x0000FFFF]->vclk); break; case vclk_group_dfs: ret = &(vclk_dfs_list[id & 0x0000FFFF]->vclk); break; default: break; } return ret; } static int vclk_is_enabled(struct vclk *vclk) { if (vclk->ref_count) return 1; return 0; } /* * grpgate functions list */ static unsigned long grpgate_get(struct vclk *vclk) { struct pwrcal_vclk_grpgate *grpgate; struct pwrcal_clk *first; unsigned long ret = 0; grpgate = to_grpgate(vclk); first = grpgate->gates[0]; if (first != CLK_NONE) ret = (unsigned long)pwrcal_clk_get_rate(first); return ret; } static int grpgate_enable(struct vclk *vclk) { struct pwrcal_vclk_grpgate *grpgate; struct pwrcal_clk *cur; struct pwrcal_clk **gates_list; void *addr; unsigned int value, dirty; int i; grpgate = to_grpgate(vclk); gates_list = grpgate->gates; addr = (void *)0xFFFFFFFF; value = 0; dirty = 0; for (i = 0; gates_list[i] != CLK_NONE; i++) { cur = gates_list[i]; if (!is_gate(cur)) continue; if (addr != cur->offset) { if (dirty == 1) { pwrcal_writel(addr, value); dirty = 0; } addr = cur->offset; value = pwrcal_readl(addr); } if (value & (1 << cur->shift)) continue; value |= (1 << cur->shift); dirty = 1; } if (dirty == 1) pwrcal_writel(addr, value); return 0; } static int grpgate_disable(struct vclk *vclk) { struct pwrcal_vclk_grpgate *grpgate; struct pwrcal_clk *cur; struct pwrcal_clk **gates_list; void *addr; unsigned int value, dirty; int i; grpgate = to_grpgate(vclk); gates_list = grpgate->gates; addr = (void *)0xFFFFFFFF; value = 0; dirty = 0; for (i = 0; gates_list[i] != CLK_NONE; i++) { cur = gates_list[i]; if (!is_gate(cur)) continue; if (addr != cur->offset) { if (dirty == 1) { pwrcal_writel(addr, value); dirty = 0; } addr = cur->offset; value = pwrcal_readl(addr); } if (value & (1 << cur->shift)) { value &= ~(1 << cur->shift); dirty = 1; } } if (dirty == 1) pwrcal_writel(addr, value); return 0; } struct vclk_ops grpgate_ops = { .enable = grpgate_enable, .disable = grpgate_disable, .is_enabled = vclk_is_enabled, .get_rate = grpgate_get, }; /* * config related functions list */ int is_config(struct pwrcal_clk_set *table, int config) { int lidx; unsigned int ratio, src, enable; unsigned long long rate; int target; int diff = 0; struct pwrcal_clk *cur; for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) { cur = table[lidx].clk; target = config ? table[lidx].config1 : table[lidx].config0; if (target == -1) continue; switch (cur->id & mask_of_type) { case div_type: ratio = pwrcal_div_get_ratio(cur); if (ratio != target + 1) { if (table[lidx].config0 == -1 || table[lidx].config1 == -1) { pr_warn("check div (%s)[%d][%d]", cur->name, target + 1, ratio); } diff = 1; } break; case pll_type: rate = pwrcal_pll_get_rate(cur); if (rate != ((unsigned long long)target) * 1000) { if (table[lidx].config0 == -1 || table[lidx].config1 == -1) { do_div(rate, 1000); pr_warn("check pll. (%s)[%dKhz][%lldKhz]", cur->name, target, rate); } diff = 1; } break; case mux_type: src = pwrcal_mux_get_src(cur); if (src != target) { if (table[lidx].config0 == -1 || table[lidx].config1 == -1) { pr_warn("check mux. (%s)[%d][%d]", cur->name, target, src); } diff = 1; } break; case gate_type: enable = pwrcal_gate_is_enabled(cur); if (enable != target) { if (table[lidx].config0 == -1 || table[lidx].config1 == -1) { pr_warn("check gate. (%s)[%d][%d]", cur->name, target, enable); } diff = 1; } break; default: break; } if (diff) return 0; } return 1; } int set_config(struct pwrcal_clk_set *table, int config) { int lidx; int max; unsigned long long rate; int to; struct pwrcal_clk *cur; for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) { cur = table[lidx].clk; if (is_gate(cur)) { to = config ? table[lidx].config1 : table[lidx].config0; if (to != 1) continue; pwrcal_gate_enable(cur); } } for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) { cur = table[lidx].clk; if (is_div(cur)) { to = config ? table[lidx].config1 : table[lidx].config0; if (to == -1) continue; if (pwrcal_div_get_ratio(cur) < to + 1) if (pwrcal_div_set_ratio(cur, to + 1)) return -1; } } for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) { cur = table[lidx].clk; if (is_pll(cur)) { to = config ? table[lidx].config1 : table[lidx].config0; if (to == -1) continue; if (to == 0) { if (pwrcal_pll_disable(cur) != 0) return -1; continue; } rate = ((unsigned long long)to) * 1000; if (pwrcal_pll_get_rate(cur) > rate) { if (pwrcal_pll_set_rate(cur, rate)) return -1; if (pwrcal_pll_is_enabled(cur) == 0) if (pwrcal_pll_enable(cur)) return -1; } } } for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) { cur = table[lidx].clk; if (is_mux(cur)) { to = config ? table[lidx].config1 : table[lidx].config0; if (to == -1) continue; if (pwrcal_mux_get_src(cur) != to) if (pwrcal_mux_set_src(cur, to)) return -1; } } max = lidx; for (lidx = max - 1; lidx >= 0; lidx--) { cur = table[lidx].clk; if (is_pll(cur)) { to = config ? table[lidx].config1 : table[lidx].config0; if (to == -1) continue; if (to == 0) { if (pwrcal_pll_disable(cur)) return -1; continue; } rate = ((unsigned long long)to) * 1000; if (pwrcal_pll_get_rate(cur) < rate) { if (pwrcal_pll_set_rate(cur, rate)) return -1; if (pwrcal_pll_is_enabled(cur) == 0) if (pwrcal_pll_enable(cur)) return -1; } } } for (lidx = max - 1; lidx >= 0; lidx--) { cur = table[lidx].clk; if (is_div(cur)) { to = config ? table[lidx].config1 : table[lidx].config0; if (to == -1) continue; if (pwrcal_div_get_ratio(cur) > to + 1) if (pwrcal_div_set_ratio(cur, to + 1)) return -1; } } for (lidx = max - 1; lidx >= 0; lidx--) { cur = table[lidx].clk; if (is_gate(cur)) { to = config ? table[lidx].config1 : table[lidx].config0; if (to != 0) continue; pwrcal_gate_disable(cur); } } return 0; } /* * pxmxdx functions list */ static unsigned long pxmxdx_get(struct vclk *vclk) { struct pwrcal_vclk_pxmxdx *pxmxdx; struct pwrcal_clk *first; unsigned long ret = 0; pxmxdx = to_pxmxdx(vclk); first = pxmxdx->clk_list[0].clk; if (first != CLK_NONE) ret = (unsigned long)pwrcal_clk_get_rate(first); return ret; } static int pxmxdx_enable(struct vclk *vclk) { struct pwrcal_vclk_pxmxdx *pxmxdx; int ret; pxmxdx = to_pxmxdx(vclk); ret = set_config(pxmxdx->clk_list, 0); if (ret) pr_err("pxmxdx_enable error (%s)", vclk->name); return ret; } static int pxmxdx_disable(struct vclk *vclk) { struct pwrcal_vclk_pxmxdx *pxmxdx; int ret; pxmxdx = to_pxmxdx(vclk); ret = set_config(pxmxdx->clk_list, 1); if (ret) pr_err("pxmxdx_disable error (%s)\n", vclk->name); return ret; } struct vclk_ops pxmxdx_ops = { .enable = pxmxdx_enable, .disable = pxmxdx_disable, .is_enabled = vclk_is_enabled, .get_rate = pxmxdx_get, }; /* * umux functions list */ static unsigned long umux_get(struct vclk *vclk) { struct pwrcal_vclk_umux *umux; unsigned long ret = 0; umux = to_umux(vclk); if (umux->umux != CLK_NONE) ret = (unsigned long)pwrcal_clk_get_rate(umux->umux); return ret; } static int umux_enable(struct vclk *vclk) { struct pwrcal_vclk_umux *umux; umux = to_umux(vclk); if (umux->umux != CLK_NONE) pwrcal_mux_set_src(umux->umux, 1); return 0; } static int umux_disable(struct vclk *vclk) { struct pwrcal_vclk_umux *umux; umux = to_umux(vclk); if (umux->umux != CLK_NONE) pwrcal_mux_set_src(umux->umux, 0); return 0; } struct vclk_ops umux_ops = { .enable = umux_enable, .disable = umux_disable, .is_enabled = vclk_is_enabled, .get_rate = umux_get, }; /* * m1d1g1 functions list */ static unsigned long m1d1g1_get(struct vclk *vclk) { struct pwrcal_vclk_m1d1g1 *m1d1g1; unsigned long ret = -1; m1d1g1 = to_m1d1g1(vclk); if (m1d1g1->gate != CLK_NONE) ret = (unsigned long)pwrcal_clk_get_rate(m1d1g1->gate); else if (m1d1g1->div != CLK_NONE) ret = (unsigned long)pwrcal_clk_get_rate(m1d1g1->div); else if (m1d1g1->mux != CLK_NONE) ret = (unsigned long)pwrcal_clk_get_rate(m1d1g1->mux); return ret; } static int m1d1g1_set(struct vclk *vclk, unsigned long freq_to) { struct pwrcal_vclk_m1d1g1 *m1d1g1; int pidx, didx; struct pwrcal_clk *parents[32]; unsigned long parent_freq[32]; unsigned long long temp_freq; int min_diff = 0x7FFFFFFF; int min_diff_mux = 0x7FFFFFFF; int min_diff_div = 0x7FFFFFFF; unsigned long cur_freq; unsigned int num_of_parents; unsigned int max_ratio; int ret = -1; m1d1g1 = to_m1d1g1(vclk); if (freq_to == 0 && m1d1g1->extramux != CLK_NONE) if (pwrcal_mux_get_src(m1d1g1->extramux) != 0) if (pwrcal_mux_set_src(m1d1g1->extramux, 0)) goto out; if (freq_to == 0 && m1d1g1->gate != CLK_NONE) { if (pwrcal_gate_is_enabled(m1d1g1->gate) != 0) if (pwrcal_gate_disable(m1d1g1->gate)) goto out; goto success; } num_of_parents = pwrcal_mux_get_parents(m1d1g1->mux, parents); max_ratio = pwrcal_div_get_max_ratio(m1d1g1->div); for (pidx = 0; pidx < num_of_parents; pidx++) { temp_freq = pwrcal_clk_get_rate(parents[pidx]); parent_freq[pidx] = (unsigned long)temp_freq; } if (num_of_parents == 0) { temp_freq = pwrcal_clk_get_rate(m1d1g1->div->parent); parent_freq[0] = temp_freq; num_of_parents = 1; } if (freq_to == 0) { freq_to = parent_freq[0]; for (pidx = 0; pidx < num_of_parents; pidx++) if (freq_to > parent_freq[pidx]) freq_to = parent_freq[pidx]; if (freq_to != 0) freq_to /= (max_ratio - 1); } for (pidx = 0; pidx < num_of_parents && min_diff != 0; pidx++) { for (didx = 1; didx < max_ratio + 1 && min_diff != 0; didx++) { cur_freq = parent_freq[pidx] / didx; if (freq_to >= cur_freq) { if (min_diff > freq_to - cur_freq) { min_diff = freq_to - cur_freq; min_diff_mux = pidx; min_diff_div = didx; if (min_diff == 0) break; } } } } if (min_diff == 0x7FFFFFFF) { if (freq_to == 0) goto success; goto out; } if (m1d1g1->div != CLK_NONE) if (pwrcal_div_get_ratio(m1d1g1->div) < min_diff_div) if (pwrcal_div_set_ratio(m1d1g1->div, min_diff_div)) goto out; if (m1d1g1->mux != CLK_NONE) if (pwrcal_mux_get_src(m1d1g1->mux) != min_diff_mux) if (pwrcal_mux_set_src(m1d1g1->mux, min_diff_mux)) goto out; if (m1d1g1->div != CLK_NONE) if (pwrcal_div_get_ratio(m1d1g1->div) > min_diff_div) if (pwrcal_div_set_ratio(m1d1g1->div, min_diff_div)) goto out; if (freq_to != 0 && m1d1g1->gate != CLK_NONE) if (pwrcal_gate_is_enabled(m1d1g1->gate) != 1) if (pwrcal_gate_enable(m1d1g1->gate)) goto out; if (freq_to != 0 && m1d1g1->extramux != CLK_NONE) if (pwrcal_mux_get_src(m1d1g1->extramux) != 1) if (pwrcal_mux_set_src(m1d1g1->extramux, 1)) goto out; success: ret = 0; out: if (ret) pr_err("m1d1g1_set error(%s) (%ld)\n", vclk->name, freq_to); return ret; } static int m1d1g1_enable(struct vclk *vclk) { struct pwrcal_vclk_m1d1g1 *m1d1g1; int ret = -1; m1d1g1 = to_m1d1g1(vclk); if (m1d1g1->mux != CLK_NONE) { if (pwrcal_mux_is_enabled(m1d1g1->mux) != 1) { ret = pwrcal_mux_enable(m1d1g1->mux); if (ret) goto out; } } if (m1d1g1->div != CLK_NONE) { if (pwrcal_div_is_enabled(m1d1g1->div) != 1) { ret = pwrcal_div_enable(m1d1g1->div); if (ret) goto out; } } if (m1d1g1->gate != CLK_NONE) { if (pwrcal_gate_is_enabled(m1d1g1->gate) != 1) { ret = pwrcal_gate_enable(m1d1g1->gate); if (ret) goto out; } } if (m1d1g1->extramux != CLK_NONE) { if (pwrcal_mux_get_src(m1d1g1->extramux) != 1) { ret = pwrcal_mux_set_src(m1d1g1->extramux, 1); if (ret) goto out; } } ret = 0; out: if (ret) pr_err("m1d1g1_enable error (%s)", vclk->name); return ret; } static int m1d1g1_disable(struct vclk *vclk) { struct pwrcal_vclk_m1d1g1 *m1d1g1; int ret = 0; m1d1g1 = to_m1d1g1(vclk); if (m1d1g1->extramux != CLK_NONE) { if (pwrcal_mux_get_src(m1d1g1->extramux) != 0) { ret = pwrcal_mux_set_src(m1d1g1->extramux, 0); if (ret) goto out; } } if (m1d1g1->gate != CLK_NONE) { if (pwrcal_gate_is_enabled(m1d1g1->gate) != 0) { ret = pwrcal_gate_disable(m1d1g1->gate); if (ret) goto out; } } if (m1d1g1->mux != CLK_NONE) { if (pwrcal_mux_is_enabled(m1d1g1->mux) != 0) { ret = pwrcal_mux_disable(m1d1g1->mux); if (ret) goto out; } } out: if (ret) pr_err("pxmxdx_disable error (%s)\n", vclk->name); return ret; } struct vclk_ops m1d1g1_ops = { .enable = m1d1g1_enable, .disable = m1d1g1_disable, .is_enabled = vclk_is_enabled, .get_rate = m1d1g1_get, .set_rate = m1d1g1_set, }; /* * p1 functions list */ static unsigned long p1_get(struct vclk *vclk) { struct pwrcal_vclk_p1 *p1; unsigned long ret = 0; p1 = to_p1(vclk); ret = (unsigned long)pwrcal_pll_get_rate(p1->pll); return ret; } static int p1_set(struct vclk *vclk, unsigned long freq_to) { struct pwrcal_vclk_p1 *p1; unsigned long long temp_freq; unsigned long ret = -1; p1 = to_p1(vclk); temp_freq = (unsigned long long)freq_to; if (pwrcal_pll_get_rate(p1->pll) != temp_freq) if (pwrcal_pll_set_rate(p1->pll, temp_freq)) goto out; ret = 0; out: if (ret) pr_err("p1_set error(%s) (%ld)\n", vclk->name, freq_to); return ret; } static int p1_enable(struct vclk *vclk) { struct pwrcal_vclk_p1 *p1; int ret = -1; p1 = to_p1(vclk); ret = pwrcal_pll_enable(p1->pll); if (ret) goto out; ret = 0; out: if (ret) pr_err("p1_enable error (%s)\n", vclk->name); return ret; } static int p1_disable(struct vclk *vclk) { struct pwrcal_vclk_p1 *p1; int ret = -1; p1 = to_p1(vclk); ret = pwrcal_pll_disable(p1->pll); if (ret) goto out; ret = 0; out: if (ret) pr_err("pxmxdx_disable error (%s)\n", vclk->name); return ret; } struct vclk_ops p1_ops = { .enable = p1_enable, .disable = p1_disable, .is_enabled = vclk_is_enabled, .get_rate = p1_get, .set_rate = p1_set, }; /* * m1 functions list */ static unsigned long m1_get(struct vclk *vclk) { struct pwrcal_vclk_m1 *m1; unsigned long ret = 0; m1 = to_m1(vclk); ret = (unsigned long)pwrcal_pll_get_rate(m1->mux); return ret; } static int m1_set(struct vclk *vclk, unsigned long freq_to) { struct pwrcal_vclk_m1 *m1; unsigned long ret = -1; m1 = to_m1(vclk); if (pwrcal_mux_get_src(m1->mux) != freq_to) if (pwrcal_mux_set_src(m1->mux, freq_to)) goto out; ret = 0; out: if (ret) pr_err("m1_set error(%s) (%ld)\n", vclk->name, freq_to); return ret; } static int m1_enable(struct vclk *vclk) { struct pwrcal_vclk_m1 *m1; int ret = -1; m1 = to_m1(vclk); ret = pwrcal_mux_enable(m1->mux); if (ret) goto out; ret = 0; out: if (ret) pr_err("m1_enable error (%s)\n", vclk->name); return ret; } static int m1_disable(struct vclk *vclk) { struct pwrcal_vclk_m1 *m1 = to_m1(vclk); int ret; ret = pwrcal_mux_disable(m1->mux); if (ret) pr_err("m1_disable error (%s)\n", vclk->name); return ret; } struct vclk_ops m1_ops = { .enable = m1_enable, .disable = m1_disable, .is_enabled = vclk_is_enabled, .get_rate = m1_get, .set_rate = m1_set, }; /* * d1 functions list */ static unsigned long d1_get(struct vclk *vclk) { struct pwrcal_vclk_d1 *d1; unsigned long ret = 0; d1 = to_d1(vclk); ret = (unsigned long)pwrcal_clk_get_rate(d1->div); return ret; } static int d1_set(struct vclk *vclk, unsigned long freq_to) { struct pwrcal_vclk_d1 *d1; unsigned long long temp_freq; unsigned long long parent_freq; unsigned long long parent_temp; unsigned int ratio; struct pwrcal_clk *parent; unsigned long ret = -1; d1 = to_d1(vclk); temp_freq = (unsigned long long)freq_to; if (pwrcal_clk_get_rate(d1->div) != temp_freq) { parent = pwrcal_clk_get_parent(d1->div); parent_freq = pwrcal_clk_get_rate(parent); if (parent_freq < temp_freq) { if (pwrcal_div_set_ratio(d1->div, 1)) goto out; } else { parent_temp = parent_freq; do_div(parent_temp, temp_freq); if (parent_freq > parent_temp * temp_freq) parent_temp += 1; ratio = (unsigned int)parent_temp; if (pwrcal_div_set_ratio(d1->div, ratio)) goto out; } } ret = 0; out: if (ret) pr_err("d1_set error(%s) (%ld)\n", vclk->name, freq_to); return ret; } static int d1_enable(struct vclk *vclk) { struct pwrcal_vclk_d1 *d1; int ret = -1; d1 = to_d1(vclk); ret = pwrcal_div_enable(d1->div); if (ret) goto out; ret = 0; out: if (ret) pr_err("d1_enable error (%s)\n", vclk->name); return ret; } static int d1_disable(struct vclk *vclk) { struct pwrcal_vclk_d1 *d1; int ret = -1; d1 = to_d1(vclk); ret = pwrcal_div_disable(d1->div); if (ret) goto out; ret = 0; out: if (ret) pr_err("d1_disable error (%s)\n", vclk->name); return ret; } struct vclk_ops d1_ops = { .enable = d1_enable, .disable = d1_disable, .is_enabled = vclk_is_enabled, .get_rate = d1_get, .set_rate = d1_set, }; struct pwrcal_vclk_none vclk_0; int vclk_setrate(struct vclk *vclk, unsigned long rate) { int ret = 0; #ifdef CONFIG_EXYNOS_SNAPSHOT_CLK const char *name = "vclk_setrate"; #endif if (!vclk->ref_count && vclk->ops->set_rate) { vclk->vfreq = rate; goto out; } exynos_ss_clk(vclk, name, ESS_FLAG_IN); trace_exynos_clk_in(vclk, __func__); if (vclk->ops->set_rate) ret = vclk->ops->set_rate(vclk, rate); else ret = -1; if (!ret) { vclk->vfreq = rate; exynos_ss_clk(vclk, name, ESS_FLAG_OUT); trace_exynos_clk_out(vclk, __func__); } else { exynos_ss_clk(vclk, name, ESS_FLAG_ON); trace_exynos_clk_on(vclk, __func__); } out: return ret; } unsigned long vclk_getrate(struct vclk *vclk) { int ret = 0; #ifdef CONFIG_EXYNOS_SNAPSHOT_CLK const char *name = "vclk_getrate"; #endif exynos_ss_clk(vclk, name, ESS_FLAG_IN); trace_exynos_clk_in(vclk, __func__); if (!vclk->ref_count) { exynos_ss_clk(vclk, name, ESS_FLAG_ON); trace_exynos_clk_on(vclk, __func__); goto out; } ret = vclk->ops->get_rate(vclk); if (ret > 0) { vclk->vfreq = (unsigned long)ret; exynos_ss_clk(vclk, name, ESS_FLAG_OUT); trace_exynos_clk_out(vclk, __func__); } else { exynos_ss_clk(vclk, name, ESS_FLAG_ON); trace_exynos_clk_on(vclk, __func__); } out: return ret; } int vclk_enable(struct vclk *vclk) { int ret = 0; unsigned int tmp; #ifdef CONFIG_EXYNOS_SNAPSHOT_CLK const char *name = "vclk_enable"; #endif if (vclk->ref_count++) goto out; if (vclk->parent != VCLK_NONE) ret = vclk_enable(vclk->parent); exynos_ss_clk(vclk, name, ESS_FLAG_IN); trace_exynos_clk_in(vclk, __func__); if (ret) goto out; ret = vclk->ops->enable(vclk); if (ret || !vclk->vfreq || !vclk->ops->set_rate) goto out; tmp = vclk->vfreq; if (vclk->type == vclk_group_dfs) vclk->vfreq = 0; if (vclk->ops->set_rate(vclk, tmp)) pr_warn("vclk retenction fail (%s) (%ld)\n", vclk->name, vclk->vfreq); if (vclk->type == vclk_group_dfs) vclk->vfreq = tmp; out: if (!ret) { exynos_ss_clk(vclk, name, ESS_FLAG_OUT); trace_exynos_clk_out(vclk, __func__); } else { exynos_ss_clk(vclk, name, ESS_FLAG_ON); trace_exynos_clk_on(vclk, __func__); } return ret; } int vclk_disable(struct vclk *vclk) { int ret = 0; int parent_disable = 0; #ifdef CONFIG_EXYNOS_SNAPSHOT_CLK const char *name = "vclk_disable"; #endif if (vclk->ref_count) parent_disable = 1; if (vclk->ref_count) vclk->ref_count--; if (vclk->ref_count) goto out; exynos_ss_clk(vclk, name, ESS_FLAG_IN); trace_exynos_clk_in(vclk, __func__); ret = vclk->ops->disable(vclk); if (ret) { exynos_ss_clk(vclk, name, ESS_FLAG_ON); trace_exynos_clk_on(vclk, __func__); goto out; } else { exynos_ss_clk(vclk, name, ESS_FLAG_OUT); trace_exynos_clk_out(vclk, __func__); } if (parent_disable && vclk->parent != VCLK_NONE) ret = vclk_disable(vclk->parent); out: return ret; }