/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include #include #include #include #include #include #include #include #include #include #include #include "clock-local2.h" /* * When enabling/disabling a clock, check the halt bit up to this number * number of times (with a 1 us delay in between) before continuing. */ #define HALT_CHECK_MAX_LOOPS 500 /* For clock without halt checking, wait this long after enables/disables. */ #define HALT_CHECK_DELAY_US 10 /* * When updating an RCG configuration, check the update bit up to this number * number of times (with a 1 us delay in between) before continuing. */ #define UPDATE_CHECK_MAX_LOOPS 500 DEFINE_SPINLOCK(local_clock_reg_lock); struct clk_freq_tbl rcg_dummy_freq = F_END; #define CMD_RCGR_REG(x) (*(x)->base + (x)->cmd_rcgr_reg) #define CFG_RCGR_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x4) #define M_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x8) #define N_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0xC) #define D_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x10) #define CBCR_REG(x) (*(x)->base + (x)->cbcr_reg) #define BCR_REG(x) (*(x)->base + (x)->bcr_reg) #define VOTE_REG(x) (*(x)->base + (x)->vote_reg) /* * Important clock bit positions and masks */ #define CMD_RCGR_ROOT_ENABLE_BIT BIT(1) #define CBCR_BRANCH_ENABLE_BIT BIT(0) #define CBCR_BRANCH_OFF_BIT BIT(31) #define CMD_RCGR_CONFIG_UPDATE_BIT BIT(0) #define CMD_RCGR_ROOT_STATUS_BIT BIT(31) #define BCR_BLK_ARES_BIT BIT(0) #define CBCR_HW_CTL_BIT BIT(1) #define CFG_RCGR_DIV_MASK BM(4, 0) #define CFG_RCGR_SRC_SEL_MASK BM(10, 8) #define MND_MODE_MASK BM(13, 12) #define MND_DUAL_EDGE_MODE_BVAL BVAL(13, 12, 0x2) #define CMD_RCGR_CONFIG_DIRTY_MASK BM(7, 4) #define CBCR_CDIV_LSB 16 #define CBCR_CDIV_MSB 24 enum branch_state { BRANCH_ON, BRANCH_OFF, }; /* * RCG functions */ /* * Update an RCG with a new configuration. This may include a new M, N, or D * value, source selection or pre-divider value. * */ static void rcg_update_config(struct rcg_clk *rcg) { u32 cmd_rcgr_regval, count; cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg)); cmd_rcgr_regval |= CMD_RCGR_CONFIG_UPDATE_BIT; writel_relaxed(cmd_rcgr_regval, CMD_RCGR_REG(rcg)); /* Wait for update to take effect */ for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) { if (!(readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_CONFIG_UPDATE_BIT)) return; udelay(1); } WARN(count == 0, "%s: rcg didn't update its configuration.", rcg->c.dbg_name); } /* RCG set rate function for clocks with Half Integer Dividers. */ void set_rate_hid(struct rcg_clk *rcg, struct clk_freq_tbl *nf) { u32 cfg_regval; unsigned long flags; spin_lock_irqsave(&local_clock_reg_lock, flags); cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); cfg_regval &= ~(CFG_RCGR_DIV_MASK | CFG_RCGR_SRC_SEL_MASK); cfg_regval |= nf->div_src_val; writel_relaxed(cfg_regval, CFG_RCGR_REG(rcg)); rcg_update_config(rcg); spin_unlock_irqrestore(&local_clock_reg_lock, flags); } /* RCG set rate function for clocks with MND & Half Integer Dividers. */ void set_rate_mnd(struct rcg_clk *rcg, struct clk_freq_tbl *nf) { u32 cfg_regval; unsigned long flags; spin_lock_irqsave(&local_clock_reg_lock, flags); cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); writel_relaxed(nf->m_val, M_REG(rcg)); writel_relaxed(nf->n_val, N_REG(rcg)); writel_relaxed(nf->d_val, D_REG(rcg)); cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); cfg_regval &= ~(CFG_RCGR_DIV_MASK | CFG_RCGR_SRC_SEL_MASK); cfg_regval |= nf->div_src_val; /* Activate or disable the M/N:D divider as necessary */ cfg_regval &= ~MND_MODE_MASK; if (nf->n_val != 0) cfg_regval |= MND_DUAL_EDGE_MODE_BVAL; writel_relaxed(cfg_regval, CFG_RCGR_REG(rcg)); rcg_update_config(rcg); spin_unlock_irqrestore(&local_clock_reg_lock, flags); } static int rcg_clk_prepare(struct clk *c) { struct rcg_clk *rcg = to_rcg_clk(c); WARN(rcg->current_freq == &rcg_dummy_freq, "Attempting to prepare %s before setting its rate. " "Set the rate first!\n", rcg->c.dbg_name); return 0; } static int rcg_clk_set_rate(struct clk *c, unsigned long rate) { struct clk_freq_tbl *cf, *nf; struct rcg_clk *rcg = to_rcg_clk(c); int rc; unsigned long flags; for (nf = rcg->freq_tbl; nf->freq_hz != FREQ_END && nf->freq_hz != rate; nf++) ; if (nf->freq_hz == FREQ_END) return -EINVAL; cf = rcg->current_freq; rc = __clk_pre_reparent(c, nf->src_clk, &flags); if (rc) return rc; BUG_ON(!rcg->set_rate); /* Perform clock-specific frequency switch operations. */ rcg->set_rate(rcg, nf); rcg->current_freq = nf; c->parent = nf->src_clk; __clk_post_reparent(c, cf->src_clk, &flags); return 0; } /* * Return a supported rate that's at least the specified rate or * the max supported rate if the specified rate is larger than the * max supported rate. */ static long rcg_clk_round_rate(struct clk *c, unsigned long rate) { struct rcg_clk *rcg = to_rcg_clk(c); struct clk_freq_tbl *f; for (f = rcg->freq_tbl; f->freq_hz != FREQ_END; f++) if (f->freq_hz >= rate) return f->freq_hz; f--; return f->freq_hz; } /* Return the nth supported frequency for a given clock. */ static int rcg_clk_list_rate(struct clk *c, unsigned n) { struct rcg_clk *rcg = to_rcg_clk(c); if (!rcg->freq_tbl || rcg->freq_tbl->freq_hz == FREQ_END) return -ENXIO; return (rcg->freq_tbl + n)->freq_hz; } static struct clk *_rcg_clk_get_parent(struct rcg_clk *rcg, int has_mnd) { u32 n_regval = 0, m_regval = 0, d_regval = 0; u32 cfg_regval; struct clk_freq_tbl *freq; u32 cmd_rcgr_regval; /* Is there a pending configuration? */ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg)); if (cmd_rcgr_regval & CMD_RCGR_CONFIG_DIRTY_MASK) return NULL; /* Get values of m, n, d, div and src_sel registers. */ if (has_mnd) { m_regval = readl_relaxed(M_REG(rcg)); n_regval = readl_relaxed(N_REG(rcg)); d_regval = readl_relaxed(D_REG(rcg)); /* * The n and d values stored in the frequency tables are sign * extended to 32 bits. The n and d values in the registers are * sign extended to 8 or 16 bits. Sign extend the values read * from the registers so that they can be compared to the * values in the frequency tables. */ n_regval |= (n_regval >> 8) ? BM(31, 16) : BM(31, 8); d_regval |= (d_regval >> 8) ? BM(31, 16) : BM(31, 8); } cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); cfg_regval &= CFG_RCGR_SRC_SEL_MASK | CFG_RCGR_DIV_MASK | MND_MODE_MASK; /* If mnd counter is present, check if it's in use. */ has_mnd = (has_mnd) && ((cfg_regval & MND_MODE_MASK) == MND_DUAL_EDGE_MODE_BVAL); /* * Clear out the mn counter mode bits since we now want to compare only * the source mux selection and pre-divider values in the registers. */ cfg_regval &= ~MND_MODE_MASK; /* Figure out what rate the rcg is running at */ for (freq = rcg->freq_tbl; freq->freq_hz != FREQ_END; freq++) { if (freq->div_src_val != cfg_regval) continue; if (has_mnd) { if (freq->m_val != m_regval) continue; if (freq->n_val != n_regval) continue; if (freq->d_val != d_regval) continue; } break; } /* No known frequency found */ if (freq->freq_hz == FREQ_END) return NULL; rcg->current_freq = freq; return freq->src_clk; } static enum handoff _rcg_clk_handoff(struct rcg_clk *rcg) { u32 cmd_rcgr_regval; if (rcg->current_freq && rcg->current_freq->freq_hz != FREQ_END) rcg->c.rate = rcg->current_freq->freq_hz; /* Is the root enabled? */ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg)); if ((cmd_rcgr_regval & CMD_RCGR_ROOT_STATUS_BIT)) return HANDOFF_DISABLED_CLK; return HANDOFF_ENABLED_CLK; } static struct clk *rcg_mnd_clk_get_parent(struct clk *c) { return _rcg_clk_get_parent(to_rcg_clk(c), 1); } static struct clk *rcg_clk_get_parent(struct clk *c) { return _rcg_clk_get_parent(to_rcg_clk(c), 0); } static enum handoff rcg_mnd_clk_handoff(struct clk *c) { return _rcg_clk_handoff(to_rcg_clk(c)); } static enum handoff rcg_clk_handoff(struct clk *c) { return _rcg_clk_handoff(to_rcg_clk(c)); } #define BRANCH_CHECK_MASK BM(31, 28) #define BRANCH_ON_VAL BVAL(31, 28, 0x0) #define BRANCH_OFF_VAL BVAL(31, 28, 0x8) #define BRANCH_NOC_FSM_ON_VAL BVAL(31, 28, 0x2) /* * Branch clock functions */ static void branch_clk_halt_check(u32 halt_check, const char *clk_name, void __iomem *cbcr_reg, enum branch_state br_status) { char *status_str = (br_status == BRANCH_ON) ? "off" : "on"; /* * Use a memory barrier since some halt status registers are * not within the same 1K segment as the branch/root enable * registers. It's also needed in the udelay() case to ensure * the delay starts after the branch disable. */ mb(); if (halt_check == DELAY || halt_check == HALT_VOTED) { udelay(HALT_CHECK_DELAY_US); } else if (halt_check == HALT) { int count; u32 val; for (count = HALT_CHECK_MAX_LOOPS; count > 0; count--) { val = readl_relaxed(cbcr_reg); val &= BRANCH_CHECK_MASK; switch (br_status) { case BRANCH_ON: if (val == BRANCH_ON_VAL || val == BRANCH_NOC_FSM_ON_VAL) return; break; case BRANCH_OFF: if (val == BRANCH_OFF_VAL) return; break; }; udelay(1); } WARN(count == 0, "%s status stuck %s", clk_name, status_str); } } static int branch_clk_enable(struct clk *c) { unsigned long flags; u32 cbcr_val; struct branch_clk *branch = to_branch_clk(c); spin_lock_irqsave(&local_clock_reg_lock, flags); cbcr_val = readl_relaxed(CBCR_REG(branch)); cbcr_val |= CBCR_BRANCH_ENABLE_BIT; writel_relaxed(cbcr_val, CBCR_REG(branch)); spin_unlock_irqrestore(&local_clock_reg_lock, flags); /* Wait for clock to enable before continuing. */ branch_clk_halt_check(branch->halt_check, branch->c.dbg_name, CBCR_REG(branch), BRANCH_ON); return 0; } static void branch_clk_disable(struct clk *c) { unsigned long flags; struct branch_clk *branch = to_branch_clk(c); u32 reg_val; spin_lock_irqsave(&local_clock_reg_lock, flags); reg_val = readl_relaxed(CBCR_REG(branch)); reg_val &= ~CBCR_BRANCH_ENABLE_BIT; writel_relaxed(reg_val, CBCR_REG(branch)); spin_unlock_irqrestore(&local_clock_reg_lock, flags); /* Wait for clock to disable before continuing. */ branch_clk_halt_check(branch->halt_check, branch->c.dbg_name, CBCR_REG(branch), BRANCH_OFF); } static int branch_cdiv_set_rate(struct branch_clk *branch, unsigned long rate) { unsigned long flags; u32 regval; if (rate > branch->max_div) return -EINVAL; spin_lock_irqsave(&local_clock_reg_lock, flags); regval = readl_relaxed(CBCR_REG(branch)); regval &= ~BM(CBCR_CDIV_MSB, CBCR_CDIV_LSB); regval |= BVAL(CBCR_CDIV_MSB, CBCR_CDIV_LSB, rate); writel_relaxed(regval, CBCR_REG(branch)); spin_unlock_irqrestore(&local_clock_reg_lock, flags); return 0; } static int branch_clk_set_rate(struct clk *c, unsigned long rate) { struct branch_clk *branch = to_branch_clk(c); if (branch->max_div) return branch_cdiv_set_rate(branch, rate); if (!branch->has_sibling) return clk_set_rate(c->parent, rate); return -EPERM; } static long branch_clk_round_rate(struct clk *c, unsigned long rate) { struct branch_clk *branch = to_branch_clk(c); if (branch->max_div) return rate <= (branch->max_div) ? rate : -EPERM; if (!branch->has_sibling) return clk_round_rate(c->parent, rate); return -EPERM; } static unsigned long branch_clk_get_rate(struct clk *c) { struct branch_clk *branch = to_branch_clk(c); if (branch->max_div) return branch->c.rate; return clk_get_rate(c->parent); } static int branch_clk_list_rate(struct clk *c, unsigned n) { int level, fmax = 0, rate; struct branch_clk *branch = to_branch_clk(c); struct clk *parent = c->parent; if (branch->has_sibling == 1) return -ENXIO; if (!parent || !parent->ops->list_rate) return -ENXIO; /* Find max frequency supported within voltage constraints. */ if (!parent->vdd_class) { fmax = INT_MAX; } else { for (level = 0; level < parent->num_fmax; level++) if (parent->fmax[level]) fmax = parent->fmax[level]; } rate = parent->ops->list_rate(parent, n); if (rate <= fmax) return rate; else return -ENXIO; } static enum handoff branch_clk_handoff(struct clk *c) { struct branch_clk *branch = to_branch_clk(c); u32 cbcr_regval; cbcr_regval = readl_relaxed(CBCR_REG(branch)); if ((cbcr_regval & CBCR_BRANCH_OFF_BIT)) return HANDOFF_DISABLED_CLK; if (branch->max_div) { cbcr_regval &= BM(CBCR_CDIV_MSB, CBCR_CDIV_LSB); cbcr_regval >>= CBCR_CDIV_LSB; c->rate = cbcr_regval; } else if (!branch->has_sibling) { c->rate = clk_get_rate(c->parent); } return HANDOFF_ENABLED_CLK; } static int __branch_clk_reset(void __iomem *bcr_reg, enum clk_reset_action action) { int ret = 0; unsigned long flags; u32 reg_val; spin_lock_irqsave(&local_clock_reg_lock, flags); reg_val = readl_relaxed(bcr_reg); switch (action) { case CLK_RESET_ASSERT: reg_val |= BCR_BLK_ARES_BIT; break; case CLK_RESET_DEASSERT: reg_val &= ~BCR_BLK_ARES_BIT; break; default: ret = -EINVAL; } writel_relaxed(reg_val, bcr_reg); spin_unlock_irqrestore(&local_clock_reg_lock, flags); /* Make sure write is issued before returning. */ mb(); return ret; } static int branch_clk_reset(struct clk *c, enum clk_reset_action action) { struct branch_clk *branch = to_branch_clk(c); if (!branch->bcr_reg) return -EPERM; return __branch_clk_reset(BCR_REG(branch), action); } static int branch_clk_set_flags(struct clk *c, unsigned flags) { u32 cbcr_val; unsigned long irq_flags; struct branch_clk *branch = to_branch_clk(c); int ret = 0; spin_lock_irqsave(&local_clock_reg_lock, irq_flags); cbcr_val = readl_relaxed(CBCR_REG(branch)); switch (flags) { case CLKFLAG_RETAIN_PERIPH: cbcr_val |= BIT(13); break; case CLKFLAG_NORETAIN_PERIPH: cbcr_val &= ~BIT(13); break; case CLKFLAG_RETAIN_MEM: cbcr_val |= BIT(14); break; case CLKFLAG_NORETAIN_MEM: cbcr_val &= ~BIT(14); break; default: ret = -EINVAL; } writel_relaxed(cbcr_val, CBCR_REG(branch)); /* * 8974v2.2 has a requirement that writes to set bits 13 and 14 are * separated by at least 2 bus cycles. Cover one of these cycles by * performing an extra write here. The other cycle is covered by the * read-modify-write design of this function. */ writel_relaxed(cbcr_val, CBCR_REG(branch)); spin_unlock_irqrestore(&local_clock_reg_lock, irq_flags); /* Make sure write is issued before returning. */ mb(); return ret; } /* * Voteable clock functions */ static int local_vote_clk_reset(struct clk *c, enum clk_reset_action action) { struct local_vote_clk *vclk = to_local_vote_clk(c); if (!vclk->bcr_reg) { WARN("clk_reset called on an unsupported clock (%s)\n", c->dbg_name); return -EPERM; } return __branch_clk_reset(BCR_REG(vclk), action); } static int local_vote_clk_enable(struct clk *c) { unsigned long flags; u32 ena; struct local_vote_clk *vclk = to_local_vote_clk(c); spin_lock_irqsave(&local_clock_reg_lock, flags); ena = readl_relaxed(VOTE_REG(vclk)); ena |= vclk->en_mask; writel_relaxed(ena, VOTE_REG(vclk)); spin_unlock_irqrestore(&local_clock_reg_lock, flags); branch_clk_halt_check(vclk->halt_check, c->dbg_name, CBCR_REG(vclk), BRANCH_ON); return 0; } static void local_vote_clk_disable(struct clk *c) { unsigned long flags; u32 ena; struct local_vote_clk *vclk = to_local_vote_clk(c); spin_lock_irqsave(&local_clock_reg_lock, flags); ena = readl_relaxed(VOTE_REG(vclk)); ena &= ~vclk->en_mask; writel_relaxed(ena, VOTE_REG(vclk)); spin_unlock_irqrestore(&local_clock_reg_lock, flags); } static enum handoff local_vote_clk_handoff(struct clk *c) { struct local_vote_clk *vclk = to_local_vote_clk(c); u32 vote_regval; /* Is the branch voted on by apps? */ vote_regval = readl_relaxed(VOTE_REG(vclk)); if (!(vote_regval & vclk->en_mask)) return HANDOFF_DISABLED_CLK; return HANDOFF_ENABLED_CLK; } enum handoff byte_rcg_handoff(struct clk *clk) { struct rcg_clk *rcg = to_rcg_clk(clk); u32 div_val; unsigned long pre_div_rate, parent_rate = clk_get_rate(clk->parent); /* If the pre-divider is used, find the rate after the division */ div_val = readl_relaxed(CFG_RCGR_REG(rcg)) & CFG_RCGR_DIV_MASK; if (div_val > 1) pre_div_rate = parent_rate / ((div_val + 1) >> 1); else pre_div_rate = parent_rate; clk->rate = pre_div_rate; if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_ROOT_STATUS_BIT) return HANDOFF_DISABLED_CLK; return HANDOFF_ENABLED_CLK; } static int set_rate_byte(struct clk *clk, unsigned long rate) { struct rcg_clk *rcg = to_rcg_clk(clk); struct clk *pll = clk->parent; unsigned long source_rate, div; struct clk_freq_tbl *byte_freq = rcg->current_freq; int rc; if (rate == 0) return -EINVAL; rc = clk_set_rate(pll, rate); if (rc) return rc; source_rate = clk_round_rate(pll, rate); if ((2 * source_rate) % rate) return -EINVAL; div = ((2 * source_rate)/rate) - 1; if (div > CFG_RCGR_DIV_MASK) return -EINVAL; byte_freq->div_src_val &= ~CFG_RCGR_DIV_MASK; byte_freq->div_src_val |= BVAL(4, 0, div); set_rate_hid(rcg, byte_freq); return 0; } enum handoff pixel_rcg_handoff(struct clk *clk) { struct rcg_clk *rcg = to_rcg_clk(clk); u32 div_val = 0, mval = 0, nval = 0, cfg_regval; unsigned long pre_div_rate, parent_rate = clk_get_rate(clk->parent); cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); /* If the pre-divider is used, find the rate after the division */ div_val = cfg_regval & CFG_RCGR_DIV_MASK; if (div_val > 1) pre_div_rate = parent_rate / ((div_val + 1) >> 1); else pre_div_rate = parent_rate; clk->rate = pre_div_rate; /* * Pixel clocks have one frequency entry in their frequency table. * Update that entry. */ if (rcg->current_freq) { rcg->current_freq->div_src_val &= ~CFG_RCGR_DIV_MASK; rcg->current_freq->div_src_val |= div_val; } /* If MND is used, find the rate after the MND division */ if ((cfg_regval & MND_MODE_MASK) == MND_DUAL_EDGE_MODE_BVAL) { mval = readl_relaxed(M_REG(rcg)); nval = readl_relaxed(N_REG(rcg)); if (!nval) return HANDOFF_DISABLED_CLK; nval = (~nval) + mval; if (rcg->current_freq) { rcg->current_freq->n_val = ~(nval - mval); rcg->current_freq->m_val = mval; rcg->current_freq->d_val = ~nval; } clk->rate = (pre_div_rate * mval) / nval; } if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_ROOT_STATUS_BIT) return HANDOFF_DISABLED_CLK; return HANDOFF_ENABLED_CLK; } static int set_rate_pixel(struct clk *clk, unsigned long rate) { struct rcg_clk *rcg = to_rcg_clk(clk); struct clk *pll = clk->parent; unsigned long source_rate, div; struct clk_freq_tbl *pixel_freq = rcg->current_freq; int rc; if (rate == 0) return -EINVAL; rc = clk_set_rate(pll, rate); if (rc) return rc; source_rate = clk_round_rate(pll, rate); if ((2 * source_rate) % rate) return -EINVAL; div = ((2 * source_rate)/rate) - 1; if (div > CFG_RCGR_DIV_MASK) return -EINVAL; pixel_freq->div_src_val &= ~CFG_RCGR_DIV_MASK; pixel_freq->div_src_val |= BVAL(4, 0, div); set_rate_mnd(rcg, pixel_freq); return 0; } /* * Unlike other clocks, the HDMI rate is adjusted through PLL * re-programming. It is also routed through an HID divider. */ static int rcg_clk_set_rate_hdmi(struct clk *c, unsigned long rate) { struct clk_freq_tbl *nf; struct rcg_clk *rcg = to_rcg_clk(c); int rc; for (nf = rcg->freq_tbl; nf->freq_hz != rate; nf++) if (nf->freq_hz == FREQ_END) { rc = -EINVAL; goto out; } rc = clk_set_rate(nf->src_clk, rate); if (rc < 0) goto out; set_rate_hid(rcg, nf); rcg->current_freq = nf; c->parent = nf->src_clk; out: return rc; } #define ENABLE_REG(x) (*(x)->base + (x)->enable_reg) #define SELECT_REG(x) (*(x)->base + (x)->select_reg) /* * mux clock functions */ static void cam_mux_clk_halt_check(void) { /* Ensure that the delay starts after the mux disable/enable. */ mb(); udelay(HALT_CHECK_DELAY_US); } static int cam_mux_clk_enable(struct clk *c) { unsigned long flags; u32 regval; struct cam_mux_clk *mux = to_cam_mux_clk(c); spin_lock_irqsave(&local_clock_reg_lock, flags); regval = readl_relaxed(ENABLE_REG(mux)); regval |= mux->enable_mask; writel_relaxed(regval, ENABLE_REG(mux)); spin_unlock_irqrestore(&local_clock_reg_lock, flags); /* Wait for clock to enable before continuing. */ cam_mux_clk_halt_check(); return 0; } static void cam_mux_clk_disable(struct clk *c) { unsigned long flags; struct cam_mux_clk *mux = to_cam_mux_clk(c); u32 regval; spin_lock_irqsave(&local_clock_reg_lock, flags); regval = readl_relaxed(ENABLE_REG(mux)); regval &= ~mux->enable_mask; writel_relaxed(regval, ENABLE_REG(mux)); spin_unlock_irqrestore(&local_clock_reg_lock, flags); /* Wait for clock to disable before continuing. */ cam_mux_clk_halt_check(); } static int mux_source_switch(struct cam_mux_clk *mux, struct mux_source *dest) { unsigned long flags; u32 regval; int ret = 0; ret = __clk_pre_reparent(&mux->c, dest->clk, &flags); if (ret) goto out; regval = readl_relaxed(SELECT_REG(mux)); regval &= ~mux->select_mask; regval |= dest->select_val; writel_relaxed(regval, SELECT_REG(mux)); /* Make sure switch request goes through before proceeding. */ mb(); __clk_post_reparent(&mux->c, mux->c.parent, &flags); out: return ret; } static int cam_mux_clk_set_parent(struct clk *c, struct clk *parent) { struct cam_mux_clk *mux = to_cam_mux_clk(c); struct mux_source *dest = NULL; int ret; if (!mux->sources || !parent) return -EPERM; dest = mux->sources; while (dest->clk) { if (dest->clk == parent) break; dest++; } if (!dest->clk) return -EPERM; ret = mux_source_switch(mux, dest); if (ret) return ret; mux->c.rate = clk_get_rate(dest->clk); return 0; } static enum handoff cam_mux_clk_handoff(struct clk *c) { struct cam_mux_clk *mux = to_cam_mux_clk(c); u32 mask = mux->enable_mask; u32 regval = readl_relaxed(ENABLE_REG(mux)); c->rate = clk_get_rate(c->parent); if (mask == (regval & mask)) return HANDOFF_ENABLED_CLK; return HANDOFF_DISABLED_CLK; } static struct clk *cam_mux_clk_get_parent(struct clk *c) { struct cam_mux_clk *mux = to_cam_mux_clk(c); struct mux_source *parent = NULL; u32 regval = readl_relaxed(SELECT_REG(mux)); if (!mux->sources) return ERR_PTR(-EPERM); parent = mux->sources; while (parent->clk) { if ((regval & mux->select_mask) == parent->select_val) return parent->clk; parent++; } return ERR_PTR(-EPERM); } static int cam_mux_clk_list_rate(struct clk *c, unsigned n) { struct cam_mux_clk *mux = to_cam_mux_clk(c); int i; for (i = 0; i < n; i++) if (!mux->sources[i].clk) break; if (!mux->sources[i].clk) return -ENXIO; return clk_get_rate(mux->sources[i].clk); } struct clk_ops clk_ops_empty; struct clk_ops clk_ops_rcg = { .enable = rcg_clk_prepare, .set_rate = rcg_clk_set_rate, .list_rate = rcg_clk_list_rate, .round_rate = rcg_clk_round_rate, .handoff = rcg_clk_handoff, .get_parent = rcg_clk_get_parent, }; struct clk_ops clk_ops_rcg_mnd = { .enable = rcg_clk_prepare, .set_rate = rcg_clk_set_rate, .list_rate = rcg_clk_list_rate, .round_rate = rcg_clk_round_rate, .handoff = rcg_mnd_clk_handoff, .get_parent = rcg_mnd_clk_get_parent, }; struct clk_ops clk_ops_pixel = { .enable = rcg_clk_prepare, .set_rate = set_rate_pixel, .list_rate = rcg_clk_list_rate, .round_rate = rcg_clk_round_rate, .handoff = pixel_rcg_handoff, }; struct clk_ops clk_ops_byte = { .enable = rcg_clk_prepare, .set_rate = set_rate_byte, .list_rate = rcg_clk_list_rate, .round_rate = rcg_clk_round_rate, .handoff = byte_rcg_handoff, }; struct clk_ops clk_ops_rcg_hdmi = { .enable = rcg_clk_prepare, .set_rate = rcg_clk_set_rate_hdmi, .list_rate = rcg_clk_list_rate, .round_rate = rcg_clk_round_rate, .handoff = rcg_clk_handoff, .get_parent = rcg_clk_get_parent, }; struct clk_ops clk_ops_branch = { .enable = branch_clk_enable, .disable = branch_clk_disable, .set_rate = branch_clk_set_rate, .get_rate = branch_clk_get_rate, .list_rate = branch_clk_list_rate, .round_rate = branch_clk_round_rate, .reset = branch_clk_reset, .set_flags = branch_clk_set_flags, .handoff = branch_clk_handoff, }; struct clk_ops clk_ops_vote = { .enable = local_vote_clk_enable, .disable = local_vote_clk_disable, .reset = local_vote_clk_reset, .handoff = local_vote_clk_handoff, }; struct clk_ops clk_ops_cam_mux = { .enable = cam_mux_clk_enable, .disable = cam_mux_clk_disable, .set_parent = cam_mux_clk_set_parent, .get_parent = cam_mux_clk_get_parent, .handoff = cam_mux_clk_handoff, .list_rate = cam_mux_clk_list_rate, };