#include #include #include"clk_periph.h" /* #include"clk.h" */ static u8 sunxi_clk_periph_get_parent(struct clk_hw *hw) { u8 parent; unsigned long reg; struct sunxi_clk_periph *periph = to_clk_periph(hw); if (!periph->mux.reg) return 0; reg = periph_readl(periph, periph->mux.reg); parent = GET_BITS(periph->mux.shift, periph->mux.width, reg); return parent; } static int sunxi_clk_periph_set_parent(struct clk_hw *hw, u8 index) { unsigned long reg; struct sunxi_clk_periph *periph = to_clk_periph(hw); if (periph->flags & CLK_READONLY) return 0; if (!periph->mux.reg) return 0; reg = periph_readl(periph, periph->mux.reg); reg = SET_BITS(periph->mux.shift, periph->mux.width, reg, index); periph_writel(periph, reg, periph->mux.reg); return 0; } static int __sunxi_clk_periph_enable_shared(struct sunxi_clk_periph *periph) { unsigned long reg; struct sunxi_clk_periph_gate *gate = &periph->gate; if (!periph->com_gate) return -1; if (!periph->com_gate->val) { /* de-assert module */ if (gate->reset && !(periph->flags & CLK_IGNORE_AUTORESET) && IS_SHARE_RST_GATE(periph)) { reg = periph_readl(periph, gate->reset); reg = SET_BITS(gate->rst_shift, 1, reg, 1); periph_writel(periph, reg, gate->reset); } /* enable bus gating */ if (gate->bus && IS_SHARE_BUS_GATE(periph)) { reg = periph_readl(periph, gate->bus); reg = SET_BITS(gate->bus_shift, 1, reg, 1); periph_writel(periph, reg, gate->bus); } /* enable module gating */ if (gate->enable && IS_SHARE_MOD_GATE(periph)) { reg = periph_readl(periph, gate->enable); reg = SET_BITS(gate->enb_shift, 1, reg, 1); periph_writel(periph, reg, gate->enable); } /* enable dram gating */ if (gate->dram && IS_SHARE_MBUS_GATE(periph)) { reg = periph_readl(periph, gate->dram); reg = SET_BITS(gate->ddr_shift, 1, reg, 1); periph_writel(periph, reg, gate->dram); } } periph->com_gate->val |= 1 << periph->com_gate_off; return 0; } static int __sunxi_clk_periph_enable(struct clk_hw *hw) { unsigned long reg; struct sunxi_clk_periph *periph = to_clk_periph(hw); struct sunxi_clk_periph_gate *gate = &periph->gate; /* de-assert module */ if (gate->reset && !(periph->flags & CLK_IGNORE_AUTORESET) && !IS_SHARE_RST_GATE(periph)) { reg = periph_readl(periph, gate->reset); reg = SET_BITS(gate->rst_shift, 1, reg, 1); periph_writel(periph, reg, gate->reset); } /* enable bus gating */ if (gate->bus && !IS_SHARE_BUS_GATE(periph)) { reg = periph_readl(periph, gate->bus); reg = SET_BITS(gate->bus_shift, 1, reg, 1); periph_writel(periph, reg, gate->bus); } /* enable module gating */ if (gate->enable && !IS_SHARE_MOD_GATE(periph)) { reg = periph_readl(periph, gate->enable); if (periph->flags & CLK_REVERT_ENABLE) reg = SET_BITS(gate->enb_shift, 1, reg, 0); else reg = SET_BITS(gate->enb_shift, 1, reg, 1); periph_writel(periph, reg, gate->enable); } /* enable dram gating */ if (gate->dram && !IS_SHARE_MBUS_GATE(periph)) { reg = periph_readl(periph, gate->dram); reg = SET_BITS(gate->ddr_shift, 1, reg, 1); periph_writel(periph, reg, gate->dram); } return 0; } static int sunxi_clk_periph_enable(struct clk_hw *hw) { int ret = 0; struct sunxi_clk_periph *periph = to_clk_periph(hw); if (periph->flags & CLK_READONLY) return 0; if (periph->com_gate) ret = __sunxi_clk_periph_enable_shared(periph); if (!ret) ret = __sunxi_clk_periph_enable(hw); return ret; } static int __sunxi_clk_periph_is_enabled(struct clk_hw *hw) { int state = 1; unsigned long reg; struct sunxi_clk_periph *periph = to_clk_periph(hw); struct sunxi_clk_periph_gate *gate = &periph->gate; /* enable bus gating */ if (gate->bus) { reg = periph_readl(periph, gate->bus); state &= GET_BITS(gate->bus_shift, 1, reg); } /* enable module gating */ if (gate->enable) { reg = periph_readl(periph, gate->enable); state &= GET_BITS(gate->enb_shift, 1, reg); } /* de-assert module */ if (gate->reset) { reg = periph_readl(periph, gate->reset); state &= GET_BITS(gate->rst_shift, 1, reg); } /* enable dram gating */ if (gate->dram) { reg = periph_readl(periph, gate->dram); state &= GET_BITS(gate->ddr_shift, 1, reg); } return state; } static int sunxi_clk_periph_is_enabled(struct clk_hw *hw) { int state = 0; /* struct sunxi_clk_periph *periph = to_clk_periph(hw); */ state = __sunxi_clk_periph_is_enabled(hw); return state; } static void __sunxi_clk_periph_disable_shared(struct sunxi_clk_periph *periph) { unsigned long reg; struct sunxi_clk_periph_gate *gate = &periph->gate; if (!periph->com_gate->val) return; periph->com_gate->val &= ~(1 << periph->com_gate_off); if (!periph->com_gate->val) { /* disable dram gating */ if (gate->dram && IS_SHARE_MBUS_GATE(periph)) { reg = periph_readl(periph, gate->dram); reg = SET_BITS(gate->ddr_shift, 1, reg, 0); periph_writel(periph, reg, gate->dram); } /* disable module gating */ if (gate->enable && IS_SHARE_MOD_GATE(periph)) { reg = periph_readl(periph, gate->enable); reg = SET_BITS(gate->enb_shift, 1, reg, 0); periph_writel(periph, reg, gate->enable); } /* disable bus gating */ if (gate->bus && IS_SHARE_BUS_GATE(periph)) { reg = periph_readl(periph, gate->bus); reg = SET_BITS(gate->bus_shift, 1, reg, 0); periph_writel(periph, reg, gate->bus); } /* assert module */ if (gate->reset && !(periph->flags & CLK_IGNORE_AUTORESET) && IS_SHARE_RST_GATE(periph)) { reg = periph_readl(periph, gate->reset); reg = SET_BITS(gate->rst_shift, 1, reg, 0); periph_writel(periph, reg, gate->reset); } } } static void __sunxi_clk_periph_disable(struct sunxi_clk_periph *pperiph) { unsigned long reg; struct sunxi_clk_periph *periph = pperiph; struct sunxi_clk_periph_gate *gate = &periph->gate; /* disable dram gating */ if (gate->dram && !IS_SHARE_MBUS_GATE(periph)) { reg = periph_readl(periph, gate->dram); reg = SET_BITS(gate->ddr_shift, 1, reg, 0); periph_writel(periph, reg, gate->dram); } /* disable module gating */ if (gate->enable && !IS_SHARE_MOD_GATE(periph)) { reg = periph_readl(periph, gate->enable); if (periph->flags & CLK_REVERT_ENABLE) reg = SET_BITS(gate->enb_shift, 1, reg, 1); else reg = SET_BITS(gate->enb_shift, 1, reg, 0); periph_writel(periph, reg, gate->enable); } /* disable bus gating */ if (gate->bus && !IS_SHARE_BUS_GATE(periph)) { reg = periph_readl(periph, gate->bus); reg = SET_BITS(gate->bus_shift, 1, reg, 0); periph_writel(periph, reg, gate->bus); } /* assert module */ if (gate->reset && !(periph->flags & CLK_IGNORE_AUTORESET) && !IS_SHARE_RST_GATE(periph)) { reg = periph_readl(periph, gate->reset); reg = SET_BITS(gate->rst_shift, 1, reg, 0); periph_writel(periph, reg, gate->reset); } } static void sunxi_clk_periph_disable(struct clk_hw *hw) { struct sunxi_clk_periph *periph = to_clk_periph(hw); if (periph->flags & CLK_READONLY) return; __sunxi_clk_periph_disable(periph); if (periph->com_gate) __sunxi_clk_periph_disable_shared(periph); } static unsigned long sunxi_clk_periph_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { unsigned long reg; struct sunxi_clk_periph *periph = to_clk_periph(hw); struct sunxi_clk_periph_div *divider = &periph->divider; unsigned long div, div_m = 0, div_n = 0; unsigned long long rate = parent_rate; if (!divider->reg) return parent_rate; reg = periph_readl(periph, divider->reg); if (divider->mwidth) div_m = GET_BITS(divider->mshift, divider->mwidth, reg); if (divider->nwidth) div_n = GET_BITS(divider->nshift, divider->nwidth, reg); div = (div_m+1)*(1<divider; unsigned long i = 0, factor_m = 0, factor_n = 0, found = 0; unsigned long div, div_m = 0, div_n = 0; unsigned long long parent_rate = (*prate+rate/2-1); do_div(parent_rate, rate); div = parent_rate; if (!div) return *prate; parent_rate = *prate; div_m = 1<mwidth; if (divider->nwidth) { div_n = 1 << divider->nwidth; div_n = 1 << (div_n - 1); } else div_n = 1; while (i < (1 << divider->nwidth)) { if (div <= div_m) { factor_m = div-1; factor_n = i; do_div(parent_rate, (factor_m + 1) * (1 << factor_n)); found = 1; break; } div = div >> 1; i++; if (!div) { factor_m = 0; factor_n = i; do_div(parent_rate, (factor_m+1)*(1 << factor_n)); found = 1; break; } } if (!found) { factor_m = (div > div_m ? div_m : div) - 1; factor_n = (1 << divider->nwidth) - 1; do_div(parent_rate, (factor_m + 1) * (1 << factor_n)); } return parent_rate; } static int __sunxi_clk_periph_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { unsigned long i = 0, factor_m = 0, factor_n = 0, found = 0; unsigned long reg; struct sunxi_clk_periph *periph = to_clk_periph(hw); struct sunxi_clk_periph_div *divider = &periph->divider; unsigned long div, div_m = 0, div_n = 0; unsigned long long tmp_rate = parent_rate; if (periph->flags & CLK_READONLY) return 0; if (!divider->reg) return 0; do_div(tmp_rate, rate); div = tmp_rate; if (!div) div_m = div_n = 0; else { div_m = 1<mwidth; div_n = (1<nwidth)-1; if (div > (div_m<clk->name , rate ); */ div = div_m<nwidth)) { if (div <= div_m) { factor_m = div-1; factor_n = i; found = 1; break; } div = div >> 1; i++; if (!div) { factor_m = 0; factor_n = i; found = 1; break; } } if (!found) { factor_m = (div > div_m ? div_m : div) - 1; factor_n = (1 << divider->nwidth) - 1; } div_m = factor_m; div_n = factor_n; } reg = periph_readl(periph, divider->reg); if (divider->mwidth) reg = SET_BITS(divider->mshift, divider->mwidth, reg, div_m); if (divider->nwidth) reg = SET_BITS(divider->nshift, divider->nwidth, reg, div_n); periph_writel(periph, reg, divider->reg); return 0; } static int sunxi_clk_periph_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { int ret = 0; /* struct sunxi_clk_periph *periph = to_clk_periph(hw); */ ret = __sunxi_clk_periph_set_rate(hw, rate, parent_rate); return ret; } static const struct clk_ops sunxi_clk_periph_ops = { .get_parent = sunxi_clk_periph_get_parent, .set_parent = sunxi_clk_periph_set_parent, .recalc_rate = sunxi_clk_periph_recalc_rate, .round_rate = sunxi_clk_periph_round_rate, .set_rate = sunxi_clk_periph_set_rate, .is_enabled = sunxi_clk_periph_is_enabled, .enable = sunxi_clk_periph_enable, .disable = sunxi_clk_periph_disable, }; void sunxi_clk_get_periph_ops(struct clk_ops *ops) { memcpy(ops, &sunxi_clk_periph_ops, sizeof(sunxi_clk_periph_ops)); } int sunxi_clk_register_periph(struct periph_init_data *pd, void *base) { struct clk_init_data init; struct clk *clk; struct sunxi_clk_periph *periph; #ifdef __SUNXI_ALL_CLK_IGNORE_UNUSED__ flags |= CLK_IGNORE_UNUSED; #endif periph = pd->periph; init.name = pd->name; init.ops = periph->priv_clkops ? periph->priv_clkops : &sunxi_clk_periph_ops; init.flags = pd->flags; init.parent_names = pd->parent_names; init.num_parents = pd->num_parents; /* Data in .init is copied by clk_register(), so stack variable OK */ periph->hw.init = &init; periph->flags = init.flags; /* fix registers */ periph->mux.reg = periph->mux.reg ? (base + (u32)periph->mux.reg) : NULL; periph->divider.reg = periph->divider.reg ? (base + (u32)periph->divider.reg) : NULL; periph->gate.enable = periph->gate.enable ? (base + (u32)periph->gate.enable) : NULL; periph->gate.reset = periph->gate.reset ? (base + (u32)periph->gate.reset) : NULL; periph->gate.bus = periph->gate.bus ? (base + (u32)periph->gate.bus) : NULL; periph->gate.dram = periph->gate.dram ? (base + (u32)periph->gate.dram) : NULL; clk = clk_register(&periph->hw); if (clk == NULL) return -1; return 0; }