Skip to content

Commit

Permalink
Merge tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/kernel/git/clk/linux

Pull clk fixes from Stephen Boyd:
 "Here's more than a handful of clk driver fixes for changes that came
  in during the merge window:

   - Fix the AT91 sama5d2 programmable clk prescaler formula

   - A bunch of Amlogic meson clk driver fixes for the VPU clks

   - A DMI quirk for Intel's Bay Trail SoC's driver to properly mark pmc
     clks as critical only when really needed

   - Stop overwriting CLK_SET_RATE_PARENT flag in mediatek's clk gate
     implementation

   - Use the right structure to test for a frequency table in i.MX's
     PLL_1416x driver"

* tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux:
  clk: imx: Fix PLL_1416X not rounding rates
  clk: mediatek: fix clk-gate flag setting
  platform/x86: pmc_atom: Drop __initconst on dmi table
  clk: x86: Add system specific quirk to mark clocks as critical
  clk: meson: vid-pll-div: remove warning and return 0 on invalid config
  clk: meson: pll: fix rounding and setting a rate that matches precisely
  clk: meson-g12a: fix VPU clock parents
  clk: meson: g12a: fix VPU clock muxes mask
  clk: meson-gxbb: round the vdec dividers to closest
  clk: at91: fix programmable clock for sama5d2
  • Loading branch information
Linus Torvalds committed Apr 13, 2019
2 parents a3b8424 + f89b9e1 commit 09bad0d
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 27 deletions.
57 changes: 43 additions & 14 deletions drivers/clk/at91/clk-programmable.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
#define PROG_ID_MAX 7

#define PROG_STATUS_MASK(id) (1 << ((id) + 8))
#define PROG_PRES_MASK 0x7
#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & PROG_PRES_MASK)
#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & layout->pres_mask)
#define PROG_MAX_RM9200_CSS 3

struct clk_programmable {
Expand All @@ -37,20 +36,29 @@ static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
unsigned int pckr;
unsigned long rate;

regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);

return parent_rate >> PROG_PRES(prog->layout, pckr);
if (layout->is_pres_direct)
rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
else
rate = parent_rate >> PROG_PRES(layout, pckr);

return rate;
}

static int clk_programmable_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
struct clk_hw *parent;
long best_rate = -EINVAL;
unsigned long parent_rate;
unsigned long tmp_rate;
unsigned long tmp_rate = 0;
int shift;
int i;

Expand All @@ -60,10 +68,18 @@ static int clk_programmable_determine_rate(struct clk_hw *hw,
continue;

parent_rate = clk_hw_get_rate(parent);
for (shift = 0; shift < PROG_PRES_MASK; shift++) {
tmp_rate = parent_rate >> shift;
if (tmp_rate <= req->rate)
break;
if (layout->is_pres_direct) {
for (shift = 0; shift <= layout->pres_mask; shift++) {
tmp_rate = parent_rate / (shift + 1);
if (tmp_rate <= req->rate)
break;
}
} else {
for (shift = 0; shift < layout->pres_mask; shift++) {
tmp_rate = parent_rate >> shift;
if (tmp_rate <= req->rate)
break;
}
}

if (tmp_rate > req->rate)
Expand Down Expand Up @@ -137,16 +153,23 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
if (!div)
return -EINVAL;

shift = fls(div) - 1;
if (layout->is_pres_direct) {
shift = div - 1;

if (div != (1 << shift))
return -EINVAL;
if (shift > layout->pres_mask)
return -EINVAL;
} else {
shift = fls(div) - 1;

if (shift >= PROG_PRES_MASK)
return -EINVAL;
if (div != (1 << shift))
return -EINVAL;

if (shift >= layout->pres_mask)
return -EINVAL;
}

regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
PROG_PRES_MASK << layout->pres_shift,
layout->pres_mask << layout->pres_shift,
shift << layout->pres_shift);

return 0;
Expand Down Expand Up @@ -202,19 +225,25 @@ at91_clk_register_programmable(struct regmap *regmap,
}

const struct clk_programmable_layout at91rm9200_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 2,
.css_mask = 0x3,
.have_slck_mck = 0,
.is_pres_direct = 0,
};

const struct clk_programmable_layout at91sam9g45_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 2,
.css_mask = 0x3,
.have_slck_mck = 1,
.is_pres_direct = 0,
};

const struct clk_programmable_layout at91sam9x5_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 4,
.css_mask = 0x7,
.have_slck_mck = 0,
.is_pres_direct = 0,
};
2 changes: 2 additions & 0 deletions drivers/clk/at91/pmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,11 @@ struct clk_pll_characteristics {
};

struct clk_programmable_layout {
u8 pres_mask;
u8 pres_shift;
u8 css_mask;
u8 have_slck_mck;
u8 is_pres_direct;
};

extern const struct clk_programmable_layout at91rm9200_programmable_layout;
Expand Down
10 changes: 9 additions & 1 deletion drivers/clk/at91/sama5d2.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ static const struct {
.pll = true },
};

static const struct clk_programmable_layout sama5d2_programmable_layout = {
.pres_mask = 0xff,
.pres_shift = 4,
.css_mask = 0x7,
.have_slck_mck = 0,
.is_pres_direct = 1,
};

static void __init sama5d2_pmc_setup(struct device_node *np)
{
struct clk_range range = CLK_RANGE(0, 0);
Expand Down Expand Up @@ -249,7 +257,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i,
&at91sam9x5_programmable_layout);
&sama5d2_programmable_layout);
if (IS_ERR(hw))
goto err_free;
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/clk/imx/clk-pll14xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,

switch (pll_clk->type) {
case PLL_1416X:
if (!pll->rate_table)
if (!pll_clk->rate_table)
init.ops = &clk_pll1416x_min_ops;
else
init.ops = &clk_pll1416x_ops;
Expand Down
3 changes: 1 addition & 2 deletions drivers/clk/mediatek/clk-gate.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,10 @@ struct clk *mtk_clk_register_gate(
return ERR_PTR(-ENOMEM);

init.name = name;
init.flags = CLK_SET_RATE_PARENT;
init.flags = flags | CLK_SET_RATE_PARENT;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
init.ops = ops;
init.flags = flags;

cg->regmap = regmap;
cg->set_ofs = set_ofs;
Expand Down
2 changes: 1 addition & 1 deletion drivers/clk/meson/clk-pll.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ static bool meson_clk_pll_is_better(unsigned long rate,
return true;
} else {
/* Round down */
if (now < rate && best < now)
if (now <= rate && best < now)
return true;
}

Expand Down
6 changes: 3 additions & 3 deletions drivers/clk/meson/g12a.c
Original file line number Diff line number Diff line change
Expand Up @@ -960,14 +960,14 @@ static struct clk_regmap g12a_sd_emmc_c_clk0 = {
/* VPU Clock */

static const char * const g12a_vpu_parent_names[] = {
"fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7",
"fclk_div3", "fclk_div4", "fclk_div5", "fclk_div7",
"mpll1", "vid_pll", "hifi_pll", "gp0_pll",
};

static struct clk_regmap g12a_vpu_0_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_VPU_CLK_CNTL,
.mask = 0x3,
.mask = 0x7,
.shift = 9,
},
.hw.init = &(struct clk_init_data){
Expand Down Expand Up @@ -1011,7 +1011,7 @@ static struct clk_regmap g12a_vpu_0 = {
static struct clk_regmap g12a_vpu_1_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_VPU_CLK_CNTL,
.mask = 0x3,
.mask = 0x7,
.shift = 25,
},
.hw.init = &(struct clk_init_data){
Expand Down
2 changes: 2 additions & 0 deletions drivers/clk/meson/gxbb.c
Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,7 @@ static struct clk_regmap gxbb_vdec_1_div = {
.offset = HHI_VDEC_CLK_CNTL,
.shift = 0,
.width = 7,
.flags = CLK_DIVIDER_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "vdec_1_div",
Expand Down Expand Up @@ -2261,6 +2262,7 @@ static struct clk_regmap gxbb_vdec_hevc_div = {
.offset = HHI_VDEC2_CLK_CNTL,
.shift = 16,
.width = 7,
.flags = CLK_DIVIDER_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "vdec_hevc_div",
Expand Down
4 changes: 2 additions & 2 deletions drivers/clk/meson/vid-pll-div.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw,
div = _get_table_val(meson_parm_read(clk->map, &pll_div->val),
meson_parm_read(clk->map, &pll_div->sel));
if (!div || !div->divider) {
pr_info("%s: Invalid config value for vid_pll_div\n", __func__);
return parent_rate;
pr_debug("%s: Invalid config value for vid_pll_div\n", __func__);
return 0;
}

return DIV_ROUND_UP_ULL(parent_rate * div->multiplier, div->divider);
Expand Down
14 changes: 11 additions & 3 deletions drivers/clk/x86/clk-pmc-atom.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ static const struct clk_ops plt_clk_ops = {
};

static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
void __iomem *base,
const struct pmc_clk_data *pmc_data,
const char **parent_names,
int num_parents)
{
Expand All @@ -184,9 +184,17 @@ static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
init.num_parents = num_parents;

pclk->hw.init = &init;
pclk->reg = base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE;
pclk->reg = pmc_data->base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE;
spin_lock_init(&pclk->lock);

/*
* On some systems, the pmc_plt_clocks already enabled by the
* firmware are being marked as critical to avoid them being
* gated by the clock framework.
*/
if (pmc_data->critical && plt_clk_is_enabled(&pclk->hw))
init.flags |= CLK_IS_CRITICAL;

ret = devm_clk_hw_register(&pdev->dev, &pclk->hw);
if (ret) {
pclk = ERR_PTR(ret);
Expand Down Expand Up @@ -332,7 +340,7 @@ static int plt_clk_probe(struct platform_device *pdev)
return PTR_ERR(parent_names);

for (i = 0; i < PMC_CLK_NUM; i++) {
data->clks[i] = plt_clk_register(pdev, i, pmc_data->base,
data->clks[i] = plt_clk_register(pdev, i, pmc_data,
parent_names, data->nparents);
if (IS_ERR(data->clks[i])) {
err = PTR_ERR(data->clks[i]);
Expand Down
21 changes: 21 additions & 0 deletions drivers/platform/x86/pmc_atom.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_data/x86/clk-pmc-atom.h>
Expand Down Expand Up @@ -391,18 +392,38 @@ static int pmc_dbgfs_register(struct pmc_dev *pmc)
}
#endif /* CONFIG_DEBUG_FS */

/*
* Some systems need one or more of their pmc_plt_clks to be
* marked as critical.
*/
static const struct dmi_system_id critclk_systems[] = {
{
.ident = "MPL CEC1x",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MPL AG"),
DMI_MATCH(DMI_PRODUCT_NAME, "CEC10 Family"),
},
},
{ /*sentinel*/ }
};

static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
const struct pmc_data *pmc_data)
{
struct platform_device *clkdev;
struct pmc_clk_data *clk_data;
const struct dmi_system_id *d = dmi_first_match(critclk_systems);

clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;

clk_data->base = pmc_regmap; /* offset is added by client */
clk_data->clks = pmc_data->clks;
if (d) {
clk_data->critical = true;
pr_info("%s critclks quirk enabled\n", d->ident);
}

clkdev = platform_device_register_data(&pdev->dev, "clk-pmc-atom",
PLATFORM_DEVID_NONE,
Expand Down
3 changes: 3 additions & 0 deletions include/linux/platform_data/x86/clk-pmc-atom.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ struct pmc_clk {
*
* @base: PMC clock register base offset
* @clks: pointer to set of registered clocks, typically 0..5
* @critical: flag to indicate if firmware enabled pmc_plt_clks
* should be marked as critial or not
*/
struct pmc_clk_data {
void __iomem *base;
const struct pmc_clk *clks;
bool critical;
};

#endif /* __PLATFORM_DATA_X86_CLK_PMC_ATOM_H */

0 comments on commit 09bad0d

Please sign in to comment.