#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<nwidth; div_n = 1<<(div_n-1); } else div_n = 1; while(i < (1<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<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<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 (NULL == clk) return -1; return 0; }