#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/clk/ti.h>
#include "clock.h"
#define OMAP4_DPLL_LP_FINT_MAX 1000000
#define OMAP4_DPLL_LP_FOUT_MAX 100000000
#define OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK BIT(8)
#define OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK BIT(10)
#define OMAP4430_DPLL_REGM4XEN_MASK BIT(11)
#define OMAP4430_REGM4XEN_MULT 4
static void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)
{
u32 v;
u32 mask;
if (!clk)
return;
mask = clk->flags & CLOCK_CLKOUTX2 ?
OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg);
v &= ~mask;
ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg);
}
static void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)
{
u32 v;
u32 mask;
if (!clk)
return;
mask = clk->flags & CLOCK_CLKOUTX2 ?
OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg);
v |= mask;
ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg);
}
const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = {
.allow_idle = omap4_dpllmx_allow_gatectrl,
.deny_idle = omap4_dpllmx_deny_gatectrl,
};
static void omap4_dpll_lpmode_recalc(struct dpll_data *dd)
{
long fint, fout;
fint = clk_hw_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1);
fout = fint * dd->last_rounded_m;
if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX))
dd->last_rounded_lpmode = 1;
else
dd->last_rounded_lpmode = 0;
}
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
u32 v;
unsigned long rate;
struct dpll_data *dd;
if (!clk || !clk->dpll_data)
return 0;
dd = clk->dpll_data;
rate = omap2_get_dpll_rate(clk);
v = ti_clk_ll_ops->clk_readl(&dd->control_reg);
if (v & OMAP4430_DPLL_REGM4XEN_MASK)
rate *= OMAP4430_REGM4XEN_MULT;
return rate;
}
int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct dpll_data *dd;
if (!req->rate)
return -EINVAL;
dd = clk->dpll_data;
if (!dd)
return -EINVAL;
if (clk_hw_get_rate(dd->clk_bypass) == req->rate &&
(dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
req->best_parent_hw = dd->clk_bypass;
} else {
struct clk_rate_request tmp_req;
long r;
clk_hw_init_rate_request(hw, &tmp_req, req->rate);
dd->last_rounded_m4xen = 0;
r = omap2_dpll_determine_rate(hw, &tmp_req);
if (r < 0) {
tmp_req.rate /= OMAP4430_REGM4XEN_MULT;
r = omap2_dpll_determine_rate(hw, &tmp_req);
if (r < 0)
return r;
dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;
dd->last_rounded_m4xen = 1;
}
omap4_dpll_lpmode_recalc(dd);
req->rate = dd->last_rounded_rate;
req->best_parent_hw = dd->clk_ref;
}
req->best_parent_rate = req->rate;
return 0;
}