Commit 09bad0df authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/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
parents a3b84248 f89b9e1b
...@@ -20,8 +20,7 @@ ...@@ -20,8 +20,7 @@
#define PROG_ID_MAX 7 #define PROG_ID_MAX 7
#define PROG_STATUS_MASK(id) (1 << ((id) + 8)) #define PROG_STATUS_MASK(id) (1 << ((id) + 8))
#define PROG_PRES_MASK 0x7 #define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & layout->pres_mask)
#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & PROG_PRES_MASK)
#define PROG_MAX_RM9200_CSS 3 #define PROG_MAX_RM9200_CSS 3
struct clk_programmable { struct clk_programmable {
...@@ -37,20 +36,29 @@ static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw, ...@@ -37,20 +36,29 @@ static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate) unsigned long parent_rate)
{ {
struct clk_programmable *prog = to_clk_programmable(hw); struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
unsigned int pckr; unsigned int pckr;
unsigned long rate;
regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr); 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, static int clk_programmable_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req) 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; struct clk_hw *parent;
long best_rate = -EINVAL; long best_rate = -EINVAL;
unsigned long parent_rate; unsigned long parent_rate;
unsigned long tmp_rate; unsigned long tmp_rate = 0;
int shift; int shift;
int i; int i;
...@@ -60,10 +68,18 @@ static int clk_programmable_determine_rate(struct clk_hw *hw, ...@@ -60,10 +68,18 @@ static int clk_programmable_determine_rate(struct clk_hw *hw,
continue; continue;
parent_rate = clk_hw_get_rate(parent); parent_rate = clk_hw_get_rate(parent);
for (shift = 0; shift < PROG_PRES_MASK; shift++) { if (layout->is_pres_direct) {
tmp_rate = parent_rate >> shift; for (shift = 0; shift <= layout->pres_mask; shift++) {
if (tmp_rate <= req->rate) tmp_rate = parent_rate / (shift + 1);
break; 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) if (tmp_rate > req->rate)
...@@ -137,16 +153,23 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -137,16 +153,23 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
if (!div) if (!div)
return -EINVAL; return -EINVAL;
shift = fls(div) - 1; if (layout->is_pres_direct) {
shift = div - 1;
if (div != (1 << shift)) if (shift > layout->pres_mask)
return -EINVAL; return -EINVAL;
} else {
shift = fls(div) - 1;
if (shift >= PROG_PRES_MASK) if (div != (1 << shift))
return -EINVAL; return -EINVAL;
if (shift >= layout->pres_mask)
return -EINVAL;
}
regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), 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); shift << layout->pres_shift);
return 0; return 0;
...@@ -202,19 +225,25 @@ at91_clk_register_programmable(struct regmap *regmap, ...@@ -202,19 +225,25 @@ at91_clk_register_programmable(struct regmap *regmap,
} }
const struct clk_programmable_layout at91rm9200_programmable_layout = { const struct clk_programmable_layout at91rm9200_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 2, .pres_shift = 2,
.css_mask = 0x3, .css_mask = 0x3,
.have_slck_mck = 0, .have_slck_mck = 0,
.is_pres_direct = 0,
}; };
const struct clk_programmable_layout at91sam9g45_programmable_layout = { const struct clk_programmable_layout at91sam9g45_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 2, .pres_shift = 2,
.css_mask = 0x3, .css_mask = 0x3,
.have_slck_mck = 1, .have_slck_mck = 1,
.is_pres_direct = 0,
}; };
const struct clk_programmable_layout at91sam9x5_programmable_layout = { const struct clk_programmable_layout at91sam9x5_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 4, .pres_shift = 4,
.css_mask = 0x7, .css_mask = 0x7,
.have_slck_mck = 0, .have_slck_mck = 0,
.is_pres_direct = 0,
}; };
...@@ -71,9 +71,11 @@ struct clk_pll_characteristics { ...@@ -71,9 +71,11 @@ struct clk_pll_characteristics {
}; };
struct clk_programmable_layout { struct clk_programmable_layout {
u8 pres_mask;
u8 pres_shift; u8 pres_shift;
u8 css_mask; u8 css_mask;
u8 have_slck_mck; u8 have_slck_mck;
u8 is_pres_direct;
}; };
extern const struct clk_programmable_layout at91rm9200_programmable_layout; extern const struct clk_programmable_layout at91rm9200_programmable_layout;
......
...@@ -125,6 +125,14 @@ static const struct { ...@@ -125,6 +125,14 @@ static const struct {
.pll = true }, .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) static void __init sama5d2_pmc_setup(struct device_node *np)
{ {
struct clk_range range = CLK_RANGE(0, 0); struct clk_range range = CLK_RANGE(0, 0);
...@@ -249,7 +257,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) ...@@ -249,7 +257,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i, parent_names, 6, i,
&at91sam9x5_programmable_layout); &sama5d2_programmable_layout);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
} }
......
...@@ -362,7 +362,7 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, ...@@ -362,7 +362,7 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
switch (pll_clk->type) { switch (pll_clk->type) {
case PLL_1416X: case PLL_1416X:
if (!pll->rate_table) if (!pll_clk->rate_table)
init.ops = &clk_pll1416x_min_ops; init.ops = &clk_pll1416x_min_ops;
else else
init.ops = &clk_pll1416x_ops; init.ops = &clk_pll1416x_ops;
......
...@@ -169,11 +169,10 @@ struct clk *mtk_clk_register_gate( ...@@ -169,11 +169,10 @@ struct clk *mtk_clk_register_gate(
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
init.name = name; 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.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0; init.num_parents = parent_name ? 1 : 0;
init.ops = ops; init.ops = ops;
init.flags = flags;
cg->regmap = regmap; cg->regmap = regmap;
cg->set_ofs = set_ofs; cg->set_ofs = set_ofs;
......
...@@ -120,7 +120,7 @@ static bool meson_clk_pll_is_better(unsigned long rate, ...@@ -120,7 +120,7 @@ static bool meson_clk_pll_is_better(unsigned long rate,
return true; return true;
} else { } else {
/* Round down */ /* Round down */
if (now < rate && best < now) if (now <= rate && best < now)
return true; return true;
} }
......
...@@ -960,14 +960,14 @@ static struct clk_regmap g12a_sd_emmc_c_clk0 = { ...@@ -960,14 +960,14 @@ static struct clk_regmap g12a_sd_emmc_c_clk0 = {
/* VPU Clock */ /* VPU Clock */
static const char * const g12a_vpu_parent_names[] = { 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", "mpll1", "vid_pll", "hifi_pll", "gp0_pll",
}; };
static struct clk_regmap g12a_vpu_0_sel = { static struct clk_regmap g12a_vpu_0_sel = {
.data = &(struct clk_regmap_mux_data){ .data = &(struct clk_regmap_mux_data){
.offset = HHI_VPU_CLK_CNTL, .offset = HHI_VPU_CLK_CNTL,
.mask = 0x3, .mask = 0x7,
.shift = 9, .shift = 9,
}, },
.hw.init = &(struct clk_init_data){ .hw.init = &(struct clk_init_data){
...@@ -1011,7 +1011,7 @@ static struct clk_regmap g12a_vpu_0 = { ...@@ -1011,7 +1011,7 @@ static struct clk_regmap g12a_vpu_0 = {
static struct clk_regmap g12a_vpu_1_sel = { static struct clk_regmap g12a_vpu_1_sel = {
.data = &(struct clk_regmap_mux_data){ .data = &(struct clk_regmap_mux_data){
.offset = HHI_VPU_CLK_CNTL, .offset = HHI_VPU_CLK_CNTL,
.mask = 0x3, .mask = 0x7,
.shift = 25, .shift = 25,
}, },
.hw.init = &(struct clk_init_data){ .hw.init = &(struct clk_init_data){
......
...@@ -2216,6 +2216,7 @@ static struct clk_regmap gxbb_vdec_1_div = { ...@@ -2216,6 +2216,7 @@ static struct clk_regmap gxbb_vdec_1_div = {
.offset = HHI_VDEC_CLK_CNTL, .offset = HHI_VDEC_CLK_CNTL,
.shift = 0, .shift = 0,
.width = 7, .width = 7,
.flags = CLK_DIVIDER_ROUND_CLOSEST,
}, },
.hw.init = &(struct clk_init_data){ .hw.init = &(struct clk_init_data){
.name = "vdec_1_div", .name = "vdec_1_div",
...@@ -2261,6 +2262,7 @@ static struct clk_regmap gxbb_vdec_hevc_div = { ...@@ -2261,6 +2262,7 @@ static struct clk_regmap gxbb_vdec_hevc_div = {
.offset = HHI_VDEC2_CLK_CNTL, .offset = HHI_VDEC2_CLK_CNTL,
.shift = 16, .shift = 16,
.width = 7, .width = 7,
.flags = CLK_DIVIDER_ROUND_CLOSEST,
}, },
.hw.init = &(struct clk_init_data){ .hw.init = &(struct clk_init_data){
.name = "vdec_hevc_div", .name = "vdec_hevc_div",
......
...@@ -82,8 +82,8 @@ static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw, ...@@ -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), div = _get_table_val(meson_parm_read(clk->map, &pll_div->val),
meson_parm_read(clk->map, &pll_div->sel)); meson_parm_read(clk->map, &pll_div->sel));
if (!div || !div->divider) { if (!div || !div->divider) {
pr_info("%s: Invalid config value for vid_pll_div\n", __func__); pr_debug("%s: Invalid config value for vid_pll_div\n", __func__);
return parent_rate; return 0;
} }
return DIV_ROUND_UP_ULL(parent_rate * div->multiplier, div->divider); return DIV_ROUND_UP_ULL(parent_rate * div->multiplier, div->divider);
......
...@@ -165,7 +165,7 @@ static const struct clk_ops plt_clk_ops = { ...@@ -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, 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, const char **parent_names,
int num_parents) int num_parents)
{ {
...@@ -184,9 +184,17 @@ static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id, ...@@ -184,9 +184,17 @@ static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
init.num_parents = num_parents; init.num_parents = num_parents;
pclk->hw.init = &init; 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); 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); ret = devm_clk_hw_register(&pdev->dev, &pclk->hw);
if (ret) { if (ret) {
pclk = ERR_PTR(ret); pclk = ERR_PTR(ret);
...@@ -332,7 +340,7 @@ static int plt_clk_probe(struct platform_device *pdev) ...@@ -332,7 +340,7 @@ static int plt_clk_probe(struct platform_device *pdev)
return PTR_ERR(parent_names); return PTR_ERR(parent_names);
for (i = 0; i < PMC_CLK_NUM; i++) { 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); parent_names, data->nparents);
if (IS_ERR(data->clks[i])) { if (IS_ERR(data->clks[i])) {
err = PTR_ERR(data->clks[i]); err = PTR_ERR(data->clks[i]);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/dmi.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/platform_data/x86/clk-pmc-atom.h> #include <linux/platform_data/x86/clk-pmc-atom.h>
...@@ -391,11 +392,27 @@ static int pmc_dbgfs_register(struct pmc_dev *pmc) ...@@ -391,11 +392,27 @@ static int pmc_dbgfs_register(struct pmc_dev *pmc)
} }
#endif /* CONFIG_DEBUG_FS */ #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, static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
const struct pmc_data *pmc_data) const struct pmc_data *pmc_data)
{ {
struct platform_device *clkdev; struct platform_device *clkdev;
struct pmc_clk_data *clk_data; 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); clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
if (!clk_data) if (!clk_data)
...@@ -403,6 +420,10 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap, ...@@ -403,6 +420,10 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
clk_data->base = pmc_regmap; /* offset is added by client */ clk_data->base = pmc_regmap; /* offset is added by client */
clk_data->clks = pmc_data->clks; 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", clkdev = platform_device_register_data(&pdev->dev, "clk-pmc-atom",
PLATFORM_DEVID_NONE, PLATFORM_DEVID_NONE,
......
...@@ -35,10 +35,13 @@ struct pmc_clk { ...@@ -35,10 +35,13 @@ struct pmc_clk {
* *
* @base: PMC clock register base offset * @base: PMC clock register base offset
* @clks: pointer to set of registered clocks, typically 0..5 * @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 { struct pmc_clk_data {
void __iomem *base; void __iomem *base;
const struct pmc_clk *clks; const struct pmc_clk *clks;
bool critical;
}; };
#endif /* __PLATFORM_DATA_X86_CLK_PMC_ATOM_H */ #endif /* __PLATFORM_DATA_X86_CLK_PMC_ATOM_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment