[PATCH v3 3/4] clk: bcm2835: Add support for programming the audio domain clocks.
From: Stephen Boyd <hidden>
Date: 2015-10-02 00:07:46
Also in:
linux-clk, linux-devicetree, lkml
On 09/28, Eric Anholt wrote:
+
+static const char *bcm2835_clock_per_parents[] = {
+static const char *bcm2835_clock_vpu_parents[] = {
+static const char *bcm2835_clock_osc_parents[] = {Can these parent arrays be const char * const ?
+ "gnd",
+ "xosc",
+ "testdebug0",
+ "testdebug1"
+};
+
+/*
+ * Used for a 1Mhz clock for the system clocksource, and also used by
+ * the watchdog timer and the camera pulse generator.
+ */
+static struct bcm2835_clock_data bcm2835_clock_timer_data = {
+static struct bcm2835_clock_data bcm2835_clock_otp_data = {
+static struct bcm2835_clock_data bcm2835_clock_vpu_data = {
+static struct bcm2835_clock_data bcm2835_clock_v3d_data = {
+static struct bcm2835_clock_data bcm2835_clock_isp_data = {
+static struct bcm2835_clock_data bcm2835_clock_h264_data = {
+static struct bcm2835_clock_data bcm2835_clock_vec_data = {
+static struct bcm2835_clock_data bcm2835_clock_uart_data = {
+static struct bcm2835_clock_data bcm2835_clock_hsm_data = {
+static struct bcm2835_clock_data bcm2835_clock_sdram_data = {
+static struct bcm2835_clock_data bcm2835_clock_tsens_data = {
+static struct bcm2835_clock_data bcm2835_clock_emmc_data = {Can all these data structures be const?
+static int bcm2835_pll_is_on(struct clk_hw *hw)
+{
+ struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
+ struct bcm2835_cprman *cprman = pll->cprman;
+ const struct bcm2835_pll_data *data = pll->data;
+
+ return (cprman_read(cprman, data->a2w_ctrl_reg) &
+ A2W_PLL_CTRL_PRST_DISABLE);Useless parenthesis.
+}
+
+static void bcm2835_pll_choose_ndiv_and_fdiv(unsigned long rate,
+ unsigned long parent_rate,
+ u32 *ndiv, u32 *fdiv)
+{
+ u64 div;
+
+ div = ((u64)rate << A2W_PLL_FRAC_BITS);
+ do_div(div, parent_rate);
+
+ *ndiv = div >> A2W_PLL_FRAC_BITS;
+ *fdiv = div & ((1 << A2W_PLL_FRAC_BITS) - 1);
+}[..]
+static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
+ struct bcm2835_cprman *cprman = pll->cprman;
+ const struct bcm2835_pll_data *data = pll->data;
+ u32 a2wctrl = cprman_read(cprman, data->a2w_ctrl_reg);
+ u32 ndiv, pdiv, fdiv;
+
+ if (parent_rate == 0)
+ return 0;
+
+ fdiv = cprman_read(cprman, data->frac_reg) & A2W_PLL_FRAC_MASK;
+ ndiv = (a2wctrl & A2W_PLL_CTRL_NDIV_MASK) >> A2W_PLL_CTRL_NDIV_SHIFT;
+ pdiv = (a2wctrl & A2W_PLL_CTRL_PDIV_MASK) >> A2W_PLL_CTRL_PDIV_SHIFT;
+
+ if (cprman_read(cprman, data->ana_reg_base + 4) &
+ data->ana->fb_prediv_mask) {
+ ndiv *= 2;
+ }How about a local variable so that we can put the if on one line and drop the braces?
+ + return bcm2835_pll_rate_from_divisors(parent_rate, ndiv, fdiv, pdiv); +} +
[..]
+static int bcm2835_pll_on(struct clk_hw *hw)
+{
+ struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
+ struct bcm2835_cprman *cprman = pll->cprman;
+ const struct bcm2835_pll_data *data = pll->data;
+
+ /* Take the PLL out of reset. */
+ cprman_write(cprman, data->cm_ctrl_reg,
+ cprman_read(cprman, data->cm_ctrl_reg) & ~CM_PLL_ANARST);
+
+ /* Wait for the PLL to lock. */
+ while (!(cprman_read(cprman, CM_LOCK) & data->lock_mask))
+ cpu_relax();Is there any reasonable timeout that we can put here? Hopefully this isn't an infinite loop.
+
+ return 0;
+}
+
+static int bcm2835_pll_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate)
+{[..]
+
+ /* Unmask the reference clock from the oscillator. */
+ cprman_write(cprman, A2W_XOSC_CTRL,
+ cprman_read(cprman, A2W_XOSC_CTRL) |
+ data->reference_enable_mask);
+
+ if (do_ana_setup_first) {
+ cprman_write(cprman, data->ana_reg_base + 12, ana3);
+ cprman_write(cprman, data->ana_reg_base + 8, ana2);ana2 never changes, so why do we need to write it again?
+ cprman_write(cprman, data->ana_reg_base + 4, ana1); + cprman_write(cprman, data->ana_reg_base + 0, ana0);
Maybe this should be a function that takes a u32 array of size 4.
+ } + + /* Set the PLL multiplier from the oscillator. */ + cprman_write(cprman, data->frac_reg, fdiv); + cprman_write(cprman, data->a2w_ctrl_reg, + (cprman_read(cprman, data->a2w_ctrl_reg) & + ~(A2W_PLL_CTRL_NDIV_MASK | + A2W_PLL_CTRL_PDIV_MASK)) | + (ndiv << A2W_PLL_CTRL_NDIV_SHIFT) | + (pdiv << A2W_PLL_CTRL_PDIV_SHIFT));
This is a 6 line write. Can we get some local variables and do the bit setting in different statements?
+
+ if (!do_ana_setup_first) {
+ cprman_write(cprman, data->ana_reg_base + 12, ana3);
+ cprman_write(cprman, data->ana_reg_base + 8, ana2);
+ cprman_write(cprman, data->ana_reg_base + 4, ana1);
+ cprman_write(cprman, data->ana_reg_base + 0, ana0);Function would help because we do this twice.
+ } + + bcm2835_pll_get_rate(&pll->hw, parent_rate); + + return 0; +} +
[...]
+static unsigned long bcm2835_pll_divider_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct bcm2835_pll_divider *divider =
+ container_of(hw, struct bcm2835_pll_divider, div.hw);
+ struct bcm2835_cprman *cprman = divider->cprman;
+ const struct bcm2835_pll_divider_data *data = divider->data;
+ u32 div = cprman_read(cprman, data->a2w_reg);
+
+ div &= ((1 << A2W_PLL_DIV_BITS) - 1);One too many parenthesis here.
+ if (div == 0) + div = 256; + + return parent_rate / div; +} +
[..]
+
+static int bcm2835_clock_is_on(struct clk_hw *hw)
+{
+ struct bcm2835_clock *clock =
+ container_of(hw, struct bcm2835_clock, hw);
+ struct bcm2835_cprman *cprman = clock->cprman;
+ const struct bcm2835_clock_data *data = clock->data;
+
+ /*
+ * The VPU clock is always on, regardless of what we might set
+ * the enable bit to.
+ */
+ if (data->is_nonstop)Maybe the variable should be called is_vpu_clock then? Or the comment is going to go out of date soon.
+ return true;
+
+ return (cprman_read(cprman, data->ctl_reg) & CM_ENABLE) != 0;
+}
+
+static u32 bcm2835_clock_choose_div(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct bcm2835_clock *clock =
+ container_of(hw, struct bcm2835_clock, hw);
+ const struct bcm2835_clock_data *data = clock->data;
+ u32 unused_frac_mask = (1 << (CM_DIV_FRAC_BITS - data->frac_bits)) - 1;We have GENMASK for this sort of stuff.
+ u64 temp = (u64)parent_rate << CM_DIV_FRAC_BITS;
+ u32 div;
+
+ do_div(temp, rate);
+ div = temp;
+
+ /* Round and mask off the unused bits */
+ if (unused_frac_mask != 0) {
+ div += unused_frac_mask >> 1;
+ div &= ~unused_frac_mask;
+ }
+
+ /* Clamp to the limits. */
+ div = max(div, unused_frac_mask + 1);
+ div = min(div, (((1 << (data->int_bits + CM_DIV_FRAC_BITS)) - 1)) &
+ ~unused_frac_mask);
+
+ return div;
+}
+
+static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
+ unsigned long parent_rate,
+ u32 div)
+{
+ const struct bcm2835_clock_data *data = clock->data;
+ u64 temp;
+
+ /*
+ * The divisor is a 12.12 fixed point field, but only some of
+ * the bits are populated in any given clock.
+ */
+ div >>= (CM_DIV_FRAC_BITS - data->frac_bits);Useless parenthesis here.
+ div &= (1 << (data->int_bits + data->frac_bits)) - 1;
+
+ if (div == 0)
+ return 0;
+
+ temp = (u64)parent_rate << data->frac_bits;
+
+ do_div(temp, div);
+
+ return temp;
+}
+
+static long bcm2835_clock_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct bcm2835_clock *clock =
+ container_of(hw, struct bcm2835_clock, hw);Would be nice to have a macro to get this onto one line struct bcm2835_clock *clock = to_bcm2385_clock(hw);
+ u32 div = bcm2835_clock_choose_div(hw, rate, *parent_rate);
+
+ return bcm2835_clock_rate_from_divisor(clock, *parent_rate, div);
+}
+
+static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct bcm2835_clock *clock =
+ container_of(hw, struct bcm2835_clock, hw);
+ struct bcm2835_cprman *cprman = clock->cprman;
+ const struct bcm2835_clock_data *data = clock->data;
+ u32 div = cprman_read(cprman, data->div_reg);
+
+ return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
+}
+
+static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock)
+{
+ struct bcm2835_cprman *cprman = clock->cprman;
+ const struct bcm2835_clock_data *data = clock->data;
+
+ while (cprman_read(cprman, data->ctl_reg) & CM_BUSY)
+ cpu_relax();
+}
+
+static void bcm2835_clock_off(struct clk_hw *hw)
+{
+ struct bcm2835_clock *clock =
+ container_of(hw, struct bcm2835_clock, hw);
+ struct bcm2835_cprman *cprman = clock->cprman;
+ const struct bcm2835_clock_data *data = clock->data;
+
+ if (data->is_nonstop)Or we should have different clk_ops for clocks that are "nonstop" so that we don't do any sorts of checks here.
+ return; + + spin_lock(&cprman->regs_lock); + cprman_write(cprman, data->ctl_reg, + cprman_read(cprman, data->ctl_reg) & ~CM_ENABLE); + spin_unlock(&cprman->regs_lock); + + /* BUSY will remain high until the divider completes its cycle. */ + bcm2835_clock_wait_busy(clock); +} +
[..]
+static struct clk *
+bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
+ const struct bcm2835_pll_divider_data *data)
+{[..]
+ clk = clk_register(cprman->dev, ÷r->div.hw);
What if clk_register() fails?
+
+ /*
+ * PLLH's channels have a fixed divide by 10 afterwards, which
+ * is what our consumers are actually using.
+ */
+ if (data->fixed_divider != 1) {
+ return clk_register_fixed_factor(cprman->dev, data->name,
+ divider_name,
+ CLK_SET_RATE_PARENT,
+ 1,
+ data->fixed_divider);
+ } else {
+ return clk;
+ }Just return clk instead of the else return clk compound statement.
+}
+
+static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman,
+ const struct bcm2835_clock_data *data)
+{
+ struct bcm2835_clock *clock;
+ struct clk_init_data init;
+ const char *parent;
+
+ /*
+ * Most of the clock generators have a mux field, so we
+ * instantiate a generic mux as our parent to handle it.
+ */
+ if (data->num_mux_parents) {
+ int i;
+
+ parent = kasprintf(GFP_KERNEL, "mux_%s", data->name);
+ if (!parent)
+ return NULL;
+
+ /*
+ * Replace our "xosc" references with the actual
+ * oscillator's name.
+ */
+ for (i = 0; i < data->num_mux_parents; i++) {
+ if (strcmp(data->parents[i], "xosc") == 0)
+ data->parents[i] = cprman->osc_name;
+ }Braces aren't needed here. -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project