[PATCH v5 06/10] clk: Add Sunplus SP7021 clock driver
Stephen Boyd
sboyd at kernel.org
Wed Dec 15 22:42:14 PST 2021
Quoting Qin Jian (2021-12-02 23:34:23)
> diff --git a/drivers/clk/clk-sp7021.c b/drivers/clk/clk-sp7021.c
> new file mode 100644
> index 000000000..040578e82
> --- /dev/null
> +++ b/drivers/clk/clk-sp7021.c
> @@ -0,0 +1,738 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +/*
> + * Copyright (C) Sunplus Technology Co., Ltd.
> + * All rights reserved.
> + */
> +//#define DEBUG
> +#include <linux/module.h>
It's not a module, don't include this.
> +#include <linux/clk.h>
Is this include used?
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/bitfield.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/delay.h>
Is this include used?
> +#include <dt-bindings/clock/sp-sp7021.h>
> +
> +#ifndef clk_readl
> +#define clk_readl readl
> +#define clk_writel writel
> +#endif
Please remove these and use readl/writel directly.
> +
> +#define REG(i) (pll_regs + (i) * 4)
> +
> +#define PLLA_CTL REG(7)
> +#define PLLE_CTL REG(12)
> +#define PLLF_CTL REG(13)
> +#define PLLTV_CTL REG(14)
> +#define PLLSYS_CTL REG(26)
> +
> +#define EXTCLK "extclk"
> +
> +/* speical div_width values for PLLTV/PLLA */
> +#define DIV_TV 33
> +#define DIV_A 34
> +
> +/* PLLTV parameters */
> +enum {
> + SEL_FRA,
> + SDM_MOD,
> + PH_SEL,
> + NFRA,
> + DIVR,
> + DIVN,
> + DIVM,
> + P_MAX
> +};
> +
> +#define MASK_SEL_FRA GENMASK(1, 1)
> +#define MASK_SDM_MOD GENMASK(2, 2)
> +#define MASK_PH_SEL GENMASK(4, 4)
> +#define MASK_NFRA GENMASK(12, 6)
> +#define MASK_DIVR GENMASK(8, 7)
> +#define MASK_DIVN GENMASK(7, 0)
> +#define MASK_DIVM GENMASK(14, 8)
> +
> +/* HIWORD_MASK FIELD_PREP */
> +#define HWM_FIELD_PREP(mask, value) \
> +({\
> + u32 m = mask; \
> + (m << 16) | FIELD_PREP(m, value); \
Please tab out the \ to a single column that's the same for all.
> +})
> +
> +struct sp_pll {
> + struct clk_hw hw;
> + void __iomem *reg;
> + spinlock_t *lock; /* lock for reg */
> + int div_shift;
> + int div_width;
> + int pd_bit; /* power down bit idx */
> + int bp_bit; /* bypass bit idx */
> + unsigned long brate; /* base rate, TODO: replace brate with muldiv */
> + u32 p[P_MAX]; /* for hold PLLTV/PLLA parameters */
> +};
> +
> +#define to_sp_pll(_hw) container_of(_hw, struct sp_pll, hw)
> +
> +#define clk_regs (moon_regs + 0x000) /* G0 ~ CLKEN */
> +#define pll_regs (moon_regs + 0x200) /* G4 ~ PLL */
> +static void __iomem *moon_regs;
> +
> +#define P_EXTCLK BIT(16)
> +static const char * const parents[] = {
Can we use clk_parent_data instead?
> + "pllsys",
> + "extclk",
> +};
> +
> +static const u32 gates[] = {
> + CLK_SYSTEM,
> + CLK_RTC,
> + CLK_IOCTL,
> + CLK_IOP,
> + CLK_OTPRX,
> + CLK_NOC,
> + CLK_BR,
> + CLK_RBUS_L00,
> + CLK_SPIFL,
> + CLK_SDCTRL0,
> + CLK_PERI0 | P_EXTCLK,
> + CLK_A926,
> + CLK_UMCTL2,
> + CLK_PERI1 | P_EXTCLK,
> +
> + CLK_DDR_PHY0,
> + CLK_ACHIP,
> + CLK_STC0,
> + CLK_STC_AV0,
> + CLK_STC_AV1,
> + CLK_STC_AV2,
> + CLK_UA0 | P_EXTCLK,
> + CLK_UA1 | P_EXTCLK,
> + CLK_UA2 | P_EXTCLK,
> + CLK_UA3 | P_EXTCLK,
> + CLK_UA4 | P_EXTCLK,
> + CLK_HWUA | P_EXTCLK,
> + CLK_DDC0,
> + CLK_UADMA | P_EXTCLK,
> +
> + CLK_CBDMA0,
> + CLK_CBDMA1,
> + CLK_SPI_COMBO_0,
> + CLK_SPI_COMBO_1,
> + CLK_SPI_COMBO_2,
> + CLK_SPI_COMBO_3,
> + CLK_AUD,
> + CLK_USBC0,
> + CLK_USBC1,
> + CLK_UPHY0,
> + CLK_UPHY1,
> +
> + CLK_I2CM0,
> + CLK_I2CM1,
> + CLK_I2CM2,
> + CLK_I2CM3,
> + CLK_PMC,
> + CLK_CARD_CTL0,
> + CLK_CARD_CTL1,
> +
> + CLK_CARD_CTL4,
> + CLK_BCH,
> + CLK_DDFCH,
> + CLK_CSIIW0,
> + CLK_CSIIW1,
> + CLK_MIPICSI0,
> + CLK_MIPICSI1,
> +
> + CLK_HDMI_TX,
> + CLK_VPOST,
> +
> + CLK_TGEN,
> + CLK_DMIX,
> + CLK_TCON,
> + CLK_INTERRUPT,
> +
> + CLK_RGST,
> + CLK_GPIO,
> + CLK_RBUS_TOP,
> +
> + CLK_MAILBOX,
> + CLK_SPIND,
> + CLK_I2C2CBUS,
> + CLK_SEC,
> + CLK_GPOST0,
> + CLK_DVE,
> +
> + CLK_OSD0,
> + CLK_DISP_PWM,
> + CLK_UADBG,
> + CLK_DUMMY_MASTER,
> + CLK_FIO_CTL,
> + CLK_FPGA,
> + CLK_L2SW,
> + CLK_ICM,
> + CLK_AXI_GLOBAL,
> +};
> +
> +static struct clk *clks[CLK_MAX];
> +static struct clk_onecell_data clk_data;
> +
> +static DEFINE_SPINLOCK(plla_lock);
> +static DEFINE_SPINLOCK(plle_lock);
> +static DEFINE_SPINLOCK(pllf_lock);
> +static DEFINE_SPINLOCK(pllsys_lock);
> +static DEFINE_SPINLOCK(plltv_lock);
> +
> +#define _M 1000000UL
> +#define F_27M (27 * _M)
> +
> +/******************************************** PLL_TV *******************************************/
> +
> +//#define PLLTV_STEP_DIR (?) /* Unit: HZ */
> +
> +/* TODO: set proper FVCO range */
> +#define FVCO_MIN (100 * _M)
> +#define FVCO_MAX (200 * _M)
> +
> +#define F_MIN (FVCO_MIN / 8)
> +#define F_MAX (FVCO_MAX)
> +
> +static long plltv_integer_div(struct sp_pll *clk, unsigned long freq)
> +{
> + /* valid m values: 27M must be divisible by m, 0 means end */
> + static const u32 m_table[] = {
> + 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 0
> + };
> + u32 m, n, r;
> +#ifdef PLLTV_STEP_DIR
> + u32 step = (PLLTV_STEP_DIR > 0) ? PLLTV_STEP_DIR : -PLLTV_STEP_DIR;
> + int calc_times = 1000000 / step;
> +#endif
> + unsigned long fvco, nf;
> +
> + /* check freq */
> + if (freq < F_MIN) {
> + pr_warn("%s: %s freq:%lu < F_MIN:%lu, round up\n",
> + __func__, clk_hw_get_name(&clk->hw), freq, F_MIN);
> + freq = F_MIN;
> + } else if (freq > F_MAX) {
> + pr_warn("%s: %s freq:%lu > F_MAX:%lu, round down\n",
> + __func__, clk_hw_get_name(&clk->hw), freq, F_MAX);
> + freq = F_MAX;
> + }
> +
> +#ifdef PLLTV_STEP_DIR
> + if ((freq % step) != 0)
> + freq += step - (freq % step) + ((PLLTV_STEP_DIR > 0) ? 0 : PLLTV_STEP_DIR);
> +#endif
> +
> +#ifdef PLLTV_STEP_DIR
> +CALC:
> + if (!calc_times) {
> + pr_err("%s: %s freq:%lu out of recalc times\n",
> + __func__, clk_hw_get_name(&clk->hw), freq);
> + return -ETIMEOUT;
> + }
> +#endif
> +
> + /* DIVR 0~3 */
> + for (r = 0; r <= 3; r++) {
> + fvco = freq << r;
> + if (fvco <= FVCO_MAX)
> + break;
> + }
> +
> + /* DIVM */
> + for (m = 0; m_table[m]; m++) {
> + nf = fvco * m_table[m];
> + n = nf / F_27M;
> + if ((n * F_27M) == nf)
> + break;
> + }
> + m = m_table[m];
> +
> + if (!m) {
> +#ifdef PLLTV_STEP_DIR
> + freq += PLLTV_STEP_DIR;
> + calc_times--;
> + goto CALC;
> +#else
Please remove the debugging code.
> + pr_err("%s: %s freq:%lu not found a valid setting\n",
> + __func__, clk_hw_get_name(&clk->hw), freq);
> + return -EINVAL;
> +#endif
> + }
> +
> + /* save parameters */
> + clk->p[SEL_FRA] = 0;
> + clk->p[DIVR] = r;
> + clk->p[DIVN] = n;
> + clk->p[DIVM] = m;
> +
> + return freq;
> +}
> +
> +/* parameters for PLLTV fractional divider */
> +static const u32 pt[][5] = {
> + /* conventional fractional */
> + {
> + 1, // factor
> + 5, // 5 * p0 (nint)
> + 1, // 1 * p0
> + F_27M, // F_27M / p0
> + 1, // p0 / p2
> + },
> + /* phase rotation */
> + {
> + 10, // factor
> + 54, // 5.4 * p0 (nint)
> + 2, // 0.2 * p0
> + F_27M / 10, // F_27M / p0
> + 5, // p0 / p2
> + },
> +};
> +
> +static const u32 mods[] = { 91, 55 }; /* SDM_MOD mod values */
> +
> +static long plltv_fractional_div(struct sp_pll *clk, unsigned long freq)
> +{
> + u32 m, r;
> + u32 nint, nfra;
> + u32 diff_min_quotient = 210000000, diff_min_remainder = 0;
> + u32 diff_min_sign = 0;
> + unsigned long fvco, nf, f, fout = 0;
> + int sdm, ph;
> +
> + /* check freq */
> + if (freq < F_MIN) {
> + pr_warn("%s: %s freq:%lu < F_MIN:%lu, round up\n",
> + __func__, clk_hw_get_name(&clk->hw), freq, F_MIN);
> + freq = F_MIN;
> + } else if (freq > F_MAX) {
> + pr_warn("%s: %s freq:%lu > F_MAX:%lu, round down\n",
> + __func__, clk_hw_get_name(&clk->hw), freq, F_MAX);
> + freq = F_MAX;
> + }
> +
> + /* DIVR 0~3 */
> + for (r = 0; r <= 3; r++) {
> + fvco = freq << r;
> + if (fvco <= FVCO_MAX)
> + break;
> + }
> + f = F_27M >> r;
> +
> + /* PH_SEL 1/0 */
> + for (ph = 1; ph >= 0; ph--) {
> + const u32 *pp = pt[ph];
> + u32 ms = 1;
> +
> + /* SDM_MOD 0/1 */
> + for (sdm = 0; sdm <= 1; sdm++) {
> + u32 mod = mods[sdm];
> +
> + /* DIVM 1~32 */
> + for (m = ms; m <= 32; m++) {
> + u32 diff_freq;
> + u32 diff_freq_quotient = 0, diff_freq_remainder = 0;
> + u32 diff_freq_sign = 0; /* 0:Positive number, 1:Negative number */
> +
> + nf = fvco * m;
> + nint = nf / pp[3];
> +
> + if (nint < pp[1])
> + continue;
> + if (nint > pp[1])
> + break;
> +
> + nfra = (((nf % pp[3]) * mod * pp[4]) + (F_27M / 2)) / F_27M;
> + if (nfra)
> + diff_freq = (f * (nint + pp[2]) / pp[0]) -
> + (f * (mod - nfra) / mod / pp[4]);
> + else
> + diff_freq = (f * (nint) / pp[0]);
> +
> + diff_freq_quotient = diff_freq / m;
> + diff_freq_remainder = ((diff_freq % m) * 1000) / m;
> +
> + if (freq > diff_freq_quotient) {
> + diff_freq_quotient = freq - diff_freq_quotient - 1;
> + diff_freq_remainder = 1000 - diff_freq_remainder;
> + diff_freq_sign = 1;
> + } else {
> + diff_freq_quotient = diff_freq_quotient - freq;
> + diff_freq_sign = 0;
> + }
> +
> + if (diff_min_quotient > diff_freq_quotient ||
> + (diff_min_quotient == diff_freq_quotient &&
> + diff_min_remainder > diff_freq_remainder)) {
> + /* found a closer freq, save parameters */
> + clk->p[SEL_FRA] = 1;
> + clk->p[SDM_MOD] = sdm;
> + clk->p[PH_SEL] = ph;
> + clk->p[NFRA] = nfra;
> + clk->p[DIVR] = r;
> + clk->p[DIVM] = m;
> +
> + fout = diff_freq / m;
> + diff_min_quotient = diff_freq_quotient;
> + diff_min_remainder = diff_freq_remainder;
> + diff_min_sign = diff_freq_sign;
> + }
> + }
> + }
> + }
> +
> + if (!fout) {
> + pr_err("%s: %s freq:%lu not found a valid setting\n",
> + __func__, clk_hw_get_name(&clk->hw), freq);
> + return -EINVAL;
> + }
> +
> + return fout;
> +}
> +
> +static long plltv_div(struct sp_pll *clk, unsigned long freq)
> +{
> + if (freq % 100)
> + return plltv_fractional_div(clk, freq);
> + else
> + return plltv_integer_div(clk, freq);
> +}
> +
> +static void plltv_set_rate(struct sp_pll *clk)
> +{
> + u32 reg;
> +
> + reg = HWM_FIELD_PREP(MASK_SEL_FRA, clk->p[SEL_FRA]);
> + reg |= HWM_FIELD_PREP(MASK_SDM_MOD, clk->p[SDM_MOD]);
> + reg |= HWM_FIELD_PREP(MASK_PH_SEL, clk->p[PH_SEL]);
> + reg |= HWM_FIELD_PREP(MASK_NFRA, clk->p[NFRA]);
> + clk_writel(reg, clk->reg);
> +
> + reg = HWM_FIELD_PREP(MASK_DIVR, clk->p[DIVR]);
> + clk_writel(reg, clk->reg + 4);
> +
> + reg = HWM_FIELD_PREP(MASK_DIVN, clk->p[DIVN] - 1);
> + reg |= HWM_FIELD_PREP(MASK_DIVM, clk->p[DIVM] - 1);
> + clk_writel(reg, clk->reg + 8);
> +}
> +
> +/******************************************** PLL_A ********************************************/
> +
> +/* from Q628_PLLs_REG_setting.xlsx */
> +struct {
> + u32 rate;
> + u32 regs[5];
> +} pa[] = {
Can this be const? and static?
> + {
> + .rate = 135475200,
> + .regs = {
> + 0x4801,
> + 0x02df,
> + 0x248f,
> + 0x0211,
> + 0x33e9
> + }
> + },
> + {
> + .rate = 147456000,
> + .regs = {
> + 0x4801,
> + 0x1adf,
> + 0x2490,
> + 0x0349,
> + 0x33e9
> + }
> + },
> + {
> + .rate = 196608000,
> + .regs = {
> + 0x4801,
> + 0x42ef,
> + 0x2495,
> + 0x01c6,
> + 0x33e9
> + }
> + },
> +};
> +
> +static void plla_set_rate(struct sp_pll *clk)
> +{
> + const u32 *pp = pa[clk->p[0]].regs;
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(pa->regs); i++)
> + clk_writel(0xffff0000 | pp[i], clk->reg + (i * 4));
> +}
> +
> +static long plla_round_rate(struct sp_pll *clk, unsigned long rate)
> +{
> + int i = ARRAY_SIZE(pa);
> +
> + while (--i) {
> + if (rate >= pa[i].rate)
> + break;
> + }
> + clk->p[0] = i;
> + return pa[i].rate;
> +}
> +
> +/******************************************* SP_PLL ********************************************/
> +
> +static long sp_pll_calc_div(struct sp_pll *clk, unsigned long rate)
> +{
> + u32 fbdiv;
> + u32 max = 1 << clk->div_width;
> +
> + fbdiv = DIV_ROUND_CLOSEST(rate, clk->brate);
> + if (fbdiv > max)
> + fbdiv = max;
> + return fbdiv;
> +}
> +
> +static long sp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *prate)
> +{
> + struct sp_pll *clk = to_sp_pll(hw);
> + long ret;
> +
> + if (rate == *prate)
> + ret = *prate; /* bypass */
> + else if (clk->div_width == DIV_A) {
> + ret = plla_round_rate(clk, rate);
> + } else if (clk->div_width == DIV_TV) {
> + ret = plltv_div(clk, rate);
> + if (ret < 0)
> + ret = *prate;
> + } else {
> + ret = sp_pll_calc_div(clk, rate) * clk->brate;
> + }
> +
> + return ret;
> +}
> +
> +static unsigned long sp_pll_recalc_rate(struct clk_hw *hw,
> + unsigned long prate)
> +{
> + struct sp_pll *clk = to_sp_pll(hw);
> + u32 reg = clk_readl(clk->reg);
> + unsigned long ret;
> +
> + if (reg & BIT(clk->bp_bit)) {
> + ret = prate; /* bypass */
> + } else if (clk->div_width == DIV_A) {
> + ret = pa[clk->p[0]].rate;
> + } else if (clk->div_width == DIV_TV) {
> + u32 m, r, reg2;
> +
> + r = FIELD_GET(MASK_DIVR, clk_readl(clk->reg + 4));
> + reg2 = clk_readl(clk->reg + 8);
> + m = FIELD_GET(MASK_DIVM, reg2) + 1;
> +
> + if (reg & BIT(1)) { /* SEL_FRA */
> + /* fractional divider */
> + u32 sdm = FIELD_GET(MASK_SDM_MOD, reg);
> + u32 ph = FIELD_GET(MASK_PH_SEL, reg);
> + u32 nfra = FIELD_GET(MASK_NFRA, reg);
> + const u32 *pp = pt[ph];
> +
> + ret = prate >> r;
> + ret = (ret * (pp[1] + pp[2]) / pp[0]) -
> + (ret * (mods[sdm] - nfra) / mods[sdm] / pp[4]);
> + ret /= m;
> + } else {
> + /* integer divider */
> + u32 n = FIELD_GET(MASK_DIVN, reg2) + 1;
> +
> + ret = (prate / m * n) >> r;
> + }
> + } else {
> + u32 fbdiv = (reg >> clk->div_shift) & ((1 << clk->div_width) - 1);
> +
> + ret = clk->brate * fbdiv;
> + }
> +
> + return ret;
> +}
> +
> +static int sp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long prate)
> +{
> + struct sp_pll *clk = to_sp_pll(hw);
> + unsigned long flags;
> + u32 reg;
> +
> + spin_lock_irqsave(clk->lock, flags);
> +
> + reg = BIT(clk->bp_bit + 16); /* HIWORD_MASK */
> +
> + if (rate == prate)
> + reg |= BIT(clk->bp_bit); /* bypass */
> + else if (clk->div_width == DIV_A)
> + plla_set_rate(clk);
> + else if (clk->div_width == DIV_TV)
> + plltv_set_rate(clk);
> + else if (clk->div_width) {
> + u32 fbdiv = sp_pll_calc_div(clk, rate);
> + u32 mask = GENMASK(clk->div_shift + clk->div_width - 1, clk->div_shift);
> +
> + reg |= (mask << 16) | (((fbdiv - 1) << clk->div_shift) & mask);
> + }
> +
> + clk_writel(reg, clk->reg);
> +
> + spin_unlock_irqrestore(clk->lock, flags);
> +
> + return 0;
> +}
> +
> +static int sp_pll_enable(struct clk_hw *hw)
> +{
> + struct sp_pll *clk = to_sp_pll(hw);
> + unsigned long flags;
> +
> + spin_lock_irqsave(clk->lock, flags);
> + clk_writel(BIT(clk->pd_bit + 16) | BIT(clk->pd_bit), clk->reg); /* power up */
> + spin_unlock_irqrestore(clk->lock, flags);
> +
> + return 0;
> +}
> +
> +static void sp_pll_disable(struct clk_hw *hw)
> +{
> + struct sp_pll *clk = to_sp_pll(hw);
> + unsigned long flags;
> +
> + spin_lock_irqsave(clk->lock, flags);
> + clk_writel(BIT(clk->pd_bit + 16), clk->reg); /* power down */
> + spin_unlock_irqrestore(clk->lock, flags);
> +}
> +
> +static int sp_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct sp_pll *clk = to_sp_pll(hw);
> +
> + return clk_readl(clk->reg) & BIT(clk->pd_bit);
> +}
> +
> +static const struct clk_ops sp_pll_ops = {
> + .enable = sp_pll_enable,
> + .disable = sp_pll_disable,
> + .is_enabled = sp_pll_is_enabled,
> + .round_rate = sp_pll_round_rate,
> + .recalc_rate = sp_pll_recalc_rate,
> + .set_rate = sp_pll_set_rate
> +};
> +
> +static const struct clk_ops sp_pll_sub_ops = {
> + .enable = sp_pll_enable,
> + .disable = sp_pll_disable,
> + .is_enabled = sp_pll_is_enabled,
> + .recalc_rate = sp_pll_recalc_rate,
> +};
> +
> +struct clk *clk_register_sp_pll(const char *name, const char *parent,
> + void __iomem *reg, int pd_bit, int bp_bit,
> + unsigned long brate, int shift, int width,
> + spinlock_t *lock)
> +{
> + struct sp_pll *pll;
> + struct clk *clk;
> + struct clk_init_data initd = {
> + .name = name,
> + .parent_names = &parent,
> + .ops = (bp_bit >= 0) ? &sp_pll_ops : &sp_pll_sub_ops,
> + .num_parents = 1,
> + .flags = CLK_IGNORE_UNUSED
Why? Preferably this flag is removed and either CLK_IS_CRITICAL is used,
with a comment indicating what is critical, or the clk is removed
entirely and driver probe just turns the clk on and forgets about it.
> + };
> +
> + pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> + if (!pll)
> + return ERR_PTR(-ENOMEM);
> +
> + if (reg == PLLSYS_CTL)
> + initd.flags |= CLK_IS_CRITICAL;
Please comment why it is critical.
> +
> + pll->hw.init = &initd;
> + pll->reg = reg;
> + pll->pd_bit = pd_bit;
> + pll->bp_bit = bp_bit;
> + pll->brate = brate;
> + pll->div_shift = shift;
> + pll->div_width = width;
> + pll->lock = lock;
> +
> + clk = clk_register(NULL, &pll->hw);
Please use clk_hw_register
> + if (WARN_ON(IS_ERR(clk))) {
> + kfree(pll);
> + } else {
> + pr_info("%-20s%lu\n", name, clk_get_rate(clk));
> + clk_register_clkdev(clk, NULL, name);
> + }
> +
> + return clk;
> +}
> +
> +static void __init sp_clk_setup(struct device_node *np)
> +{
> + int i, j;
> +
> + moon_regs = of_iomap(np, 0);
Why moon_ prefix?
> + if (WARN_ON(!moon_regs))
> + return; /* -ENXIO */
> +
> + /* PLL_A */
> + clks[PLL_A] = clk_register_sp_pll("plla", EXTCLK,
> + PLLA_CTL, 11, 12, 27000000, 0, DIV_A, &plla_lock);
> +
> + /* PLL_E */
> + clks[PLL_E] = clk_register_sp_pll("plle", EXTCLK,
> + PLLE_CTL, 6, 2, 50000000, 0, 0, &plle_lock);
> + clks[PLL_E_2P5] = clk_register_sp_pll("plle_2p5", "plle",
> + PLLE_CTL, 13, -1, 2500000, 0, 0, &plle_lock);
> + clks[PLL_E_25] = clk_register_sp_pll("plle_25", "plle",
> + PLLE_CTL, 12, -1, 25000000, 0, 0, &plle_lock);
> + clks[PLL_E_112P5] = clk_register_sp_pll("plle_112p5", "plle",
> + PLLE_CTL, 11, -1, 112500000, 0, 0, &plle_lock);
> +
> + /* PLL_F */
> + clks[PLL_F] = clk_register_sp_pll("pllf", EXTCLK,
> + PLLF_CTL, 0, 10, 13500000, 1, 4, &pllf_lock);
> +
> + /* PLL_TV */
> + clks[PLL_TV] = clk_register_sp_pll("plltv", EXTCLK,
> + PLLTV_CTL, 0, 15, 27000000, 0, DIV_TV, &plltv_lock);
> + clks[PLL_TV_A] = clk_register_divider(NULL, "plltv_a", "plltv", 0,
> + PLLTV_CTL + 4, 5, 1,
> + CLK_DIVIDER_POWER_OF_TWO, &plltv_lock);
> + clk_register_clkdev(clks[PLL_TV_A], NULL, "plltv_a");
> +
> + /* PLL_SYS */
> + clks[PLL_SYS] = clk_register_sp_pll("pllsys", EXTCLK,
> + PLLSYS_CTL, 10, 9, 13500000, 0, 4, &pllsys_lock);
> +
> + /* gates */
> + for (i = 0; i < ARRAY_SIZE(gates); i++) {
> + char s[10];
> +
> + j = gates[i] & 0xffff;
> + sprintf(s, "clken%02x", j);
> + clks[j] = clk_register_gate(NULL, s, parents[gates[i] >> 16], CLK_IGNORE_UNUSED,
Please use hw based registration.
> + clk_regs + (j >> 4) * 4, j & 0x0f,
> + CLK_GATE_HIWORD_MASK, NULL);
> + }
> +
> + clk_data.clks = clks;
> + clk_data.clk_num = ARRAY_SIZE(clks);
> + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
Please use a hw based provider instead of a clk one.
> +}
> +
> +CLK_OF_DECLARE(sp_clkc, "sunplus,sp7021-clkc", sp_clk_setup);
Why can't this be a platform driver?
> +
> +MODULE_AUTHOR("Qin Jian <qinjian at cqplus1.com>");
> +MODULE_DESCRIPTION("Sunplus SP7021 Clock Driver");
> +MODULE_LICENSE("GPL v2");
It's not a module, so these macros should be removed.
More information about the linux-arm-kernel
mailing list